|
| 1 | +<h1 align='center'>DFS - of Graph</h1> |
| 2 | + |
| 3 | +## Problem Statement |
| 4 | + |
| 5 | +**Problem URL :** [DFS of Graph](https://www.geeksforgeeks.org/problems/depth-first-traversal-for-a-graph/1) |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | + |
| 10 | +## Problem Explanation |
| 11 | +#### **Problem Description:** |
| 12 | +The problem requires you to perform a **Depth First Search (DFS)** traversal of a graph. DFS is a graph traversal algorithm that starts from a given node and explores as far as possible along each branch before backtracking. It's typically implemented using recursion (or a stack). |
| 13 | + |
| 14 | +**DFS Characteristics:** |
| 15 | +- It explores as deep as possible before backtracking. |
| 16 | +- DFS can be implemented using recursion or a stack. |
| 17 | +- It is useful for tasks like finding paths, connected components, and cycles in a graph. |
| 18 | + |
| 19 | +#### **Input:** |
| 20 | +- An adjacency list representing an undirected graph, where each node is connected to a list of its neighbors. |
| 21 | + |
| 22 | +#### **Output:** |
| 23 | +- A list of nodes in the order they are visited during a DFS traversal. |
| 24 | + |
| 25 | +#### **Example:** |
| 26 | + |
| 27 | +Consider the following graph represented as an adjacency list: |
| 28 | + |
| 29 | +``` |
| 30 | +0: [1, 2] |
| 31 | +1: [0, 3, 4] |
| 32 | +2: [0] |
| 33 | +3: [1] |
| 34 | +4: [1] |
| 35 | +``` |
| 36 | + |
| 37 | +If we start the DFS from node `0`, the expected order of nodes visited would be: |
| 38 | +``` |
| 39 | +0, 1, 3, 4, 2 |
| 40 | +``` |
| 41 | + |
| 42 | +#### **Approach:** |
| 43 | + |
| 44 | +1. **Recursive DFS Traversal:** |
| 45 | + - Start at an arbitrary node (node `0` in this case). |
| 46 | + - Mark the current node as visited. |
| 47 | + - Visit all unvisited neighbors of the current node (recursive step). |
| 48 | + - Backtrack once you’ve visited all neighbors. |
| 49 | + |
| 50 | +2. **Implementation Details:** |
| 51 | + - A **visited** map is used to ensure each node is visited only once. |
| 52 | + - A **result** vector stores the nodes in the order they are visited. |
| 53 | + |
| 54 | +3. **Process:** |
| 55 | + - Start from node `0`. |
| 56 | + - Explore neighbors recursively. |
| 57 | + - Return the traversal result. |
| 58 | + |
| 59 | +## Problem Solution |
| 60 | +```cpp |
| 61 | +class Solution { |
| 62 | + public: |
| 63 | + void dfsUtil(int node, vector<vector<int>>& adj, unordered_map<int, bool>& visited, vector<int>& result){ |
| 64 | + visited[node] = true; |
| 65 | + result.push_back(node); |
| 66 | + |
| 67 | + for(int neighbor : adj[node]){ |
| 68 | + if(!visited[neighbor]) dfsUtil(neighbor, adj, visited, result); |
| 69 | + } |
| 70 | + } |
| 71 | + |
| 72 | + vector<int> dfsOfGraph(vector<vector<int>>& adj) { |
| 73 | + int V = adj.size(); |
| 74 | + unordered_map<int, bool> visited; |
| 75 | + vector<int> result; |
| 76 | + |
| 77 | + dfsUtil(0, adj, visited, result); |
| 78 | + return result; |
| 79 | + } |
| 80 | +}; |
| 81 | +``` |
| 82 | +
|
| 83 | +## Problem Solution Explanation |
| 84 | +
|
| 85 | +```cpp |
| 86 | +class Solution { |
| 87 | + public: |
| 88 | + void dfsUtil(int node, vector<vector<int>>& adj, unordered_map<int, bool>& visited, vector<int>& result){ |
| 89 | +``` |
| 90 | +- **Line 1:** A class `Solution` is defined, which contains the main logic for DFS. |
| 91 | +- **Line 2:** The method `dfsUtil` is defined as a helper function that will recursively traverse the graph. It takes the following parameters: |
| 92 | + - `node`: the current node to start DFS from. |
| 93 | + - `adj`: the adjacency list representing the graph. |
| 94 | + - `visited`: a map to keep track of visited nodes to prevent revisiting. |
| 95 | + - `result`: a vector that stores the nodes in the order they are visited. |
| 96 | + |
| 97 | +```cpp |
| 98 | + visited[node] = true; |
| 99 | + result.push_back(node); |
| 100 | +``` |
| 101 | +- **Line 3:** Mark the current node as visited by setting `visited[node] = true`. |
| 102 | +- **Line 4:** Add the current node to the `result` vector to record the DFS traversal order. |
| 103 | + |
| 104 | +```cpp |
| 105 | + for(int neighbor : adj[node]){ |
| 106 | + if(!visited[neighbor]) dfsUtil(neighbor, adj, visited, result); |
| 107 | + } |
| 108 | +``` |
| 109 | +- **Line 5-7:** For each neighbor of the current node, check if it is visited. |
| 110 | + - If a neighbor has not been visited, recursively call `dfsUtil` on the neighbor. This continues until all neighbors are explored. |
| 111 | +
|
| 112 | +```cpp |
| 113 | + } |
| 114 | + |
| 115 | + vector<int> dfsOfGraph(vector<vector<int>>& adj) { |
| 116 | + int V = adj.size(); |
| 117 | + unordered_map<int, bool> visited; |
| 118 | + vector<int> result; |
| 119 | + |
| 120 | + dfsUtil(0, adj, visited, result); |
| 121 | + return result; |
| 122 | + } |
| 123 | +}; |
| 124 | +``` |
| 125 | +- **Line 8-10:** The function `dfsOfGraph` is the main function that initializes the visited map and result vector. It then calls `dfsUtil` starting from node `0`. |
| 126 | +- **Line 11:** Finally, the function returns the `result` vector, which contains the DFS traversal order. |
| 127 | + |
| 128 | + |
| 129 | + |
| 130 | +### **Step 3: Example with Explanation** |
| 131 | + |
| 132 | +Let's consider the graph: |
| 133 | + |
| 134 | +``` |
| 135 | +0: [1, 2] |
| 136 | +1: [0, 3, 4] |
| 137 | +2: [0] |
| 138 | +3: [1] |
| 139 | +4: [1] |
| 140 | +``` |
| 141 | + |
| 142 | +**Step-by-Step DFS Traversal:** |
| 143 | + |
| 144 | +1. **Start at node `0`:** |
| 145 | + - Mark node `0` as visited. |
| 146 | + - Add node `0` to the result: `result = [0]`. |
| 147 | + - Visit neighbors of `0`: nodes `1` and `2`. |
| 148 | + |
| 149 | +2. **Move to node `1`:** |
| 150 | + - Mark node `1` as visited. |
| 151 | + - Add node `1` to the result: `result = [0, 1]`. |
| 152 | + - Visit neighbors of `1`: nodes `0`, `3`, and `4`. |
| 153 | + - Node `0` is already visited, so we move to node `3`. |
| 154 | + |
| 155 | +3. **Move to node `3`:** |
| 156 | + - Mark node `3` as visited. |
| 157 | + - Add node `3` to the result: `result = [0, 1, 3]`. |
| 158 | + - Visit neighbors of `3`: node `1` is already visited, so we backtrack. |
| 159 | + |
| 160 | +4. **Backtrack to node `1` and move to node `4`:** |
| 161 | + - Mark node `4` as visited. |
| 162 | + - Add node `4` to the result: `result = [0, 1, 3, 4]`. |
| 163 | + - Visit neighbors of `4`: node `1` is already visited, so we backtrack. |
| 164 | + |
| 165 | +5. **Backtrack to node `0` and move to node `2`:** |
| 166 | + - Mark node `2` as visited. |
| 167 | + - Add node `2` to the result: `result = [0, 1, 3, 4, 2]`. |
| 168 | + - Visit neighbors of `2`: node `0` is already visited, so we finish. |
| 169 | + |
| 170 | +The **DFS order** is: `[0, 1, 3, 4, 2]`. |
| 171 | + |
| 172 | + |
| 173 | + |
| 174 | +### **Step 4: Time and Space Complexity** |
| 175 | + |
| 176 | +#### **Time Complexity:** |
| 177 | +- **DFS Traversal:** Each node is visited once and each edge is checked once. So, the time complexity is proportional to the number of vertices and edges. |
| 178 | +- **Time Complexity:** **`O(V + E)`**, where `V` is the number of vertices and `E` is the number of edges in the graph. |
| 179 | + |
| 180 | +#### **Space Complexity:** |
| 181 | +- **Visited Map:** We use an unordered map to track visited nodes, which takes `O(V)` space. |
| 182 | +- **Recursion Stack:** The depth of the recursion tree is at most `V` in the worst case, so the recursion stack takes `O(V)` space. |
| 183 | +- **Result Vector:** The result vector stores all the vertices, which takes `O(V)` space. |
| 184 | +- **Space Complexity:** **`O(V)`**. |
| 185 | + |
| 186 | + |
| 187 | + |
| 188 | +### **Step 5: Recommendations for Students** |
| 189 | + |
| 190 | +1. **Understand the Graph Representation:** Make sure you understand how graphs can be represented (adjacency list vs adjacency matrix). |
| 191 | +2. **Recursive vs Iterative DFS:** While this solution uses recursion, DFS can also be implemented iteratively using an explicit stack. Try implementing both to understand the differences. |
| 192 | +3. **Edge Cases:** Consider edge cases like disconnected graphs, graphs with only one node, or graphs with cycles. |
| 193 | +4. **Practice on Various Graphs:** Try running DFS on graphs of different structures (e.g., trees, cyclic graphs, and disconnected graphs) to get a solid understanding. |
| 194 | +5. **Explore Applications of DFS:** DFS is useful for finding connected components, checking for cycles, topological sorting, etc. Try solving problems involving these concepts to enhance your understanding of DFS. |
0 commit comments