Skip to content

Commit 0bf4bd6

Browse files
authored
Create README.md
1 parent 1a4b6d3 commit 0bf4bd6

File tree

1 file changed

+383
-0
lines changed
  • 23 - Graph Data Structure Problems/03 - DFS Traversal in Graph

1 file changed

+383
-0
lines changed
Lines changed: 383 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,383 @@
1+
<h1 align='center'>DFS - Traversal - In Graph</h1>
2+
3+
Depth-First Search (DFS) is a graph traversal algorithm that starts at a source node and explores as far down a branch as possible before backtracking. It systematically explores all the vertices of a graph or tree in depth.
4+
5+
In DFS:
6+
- We start at a node, explore its neighbors, then explore their neighbors recursively.
7+
- We use a **stack** (either explicitly or via recursion) to backtrack when a node has no unvisited neighbors.
8+
9+
### **DFS Characteristics:**
10+
1. **Recursive or Iterative**: DFS can be implemented using recursion (which uses the call stack) or an explicit stack.
11+
2. **Order of Exploration**: DFS explores a path to the end before trying other paths.
12+
3. **Graph Type**: DFS can be applied to both directed and undirected graphs.
13+
14+
### **Approach to DFS Traversal:**
15+
1. **Start** at a node, mark it as visited.
16+
2. **Explore** each adjacent unvisited node (neighbor) of the current node, moving deeper.
17+
3. If there are no unvisited adjacent nodes left, **backtrack** and return to the last node where there’s an unvisited neighbor.
18+
4. Repeat the process until all nodes are visited.
19+
20+
### **DFS Example:**
21+
22+
Consider the following graph:
23+
24+
```
25+
0
26+
/ \
27+
1 2
28+
/ \ \
29+
3 4 5
30+
```
31+
32+
Starting from node `0`, DFS would explore the graph as follows:
33+
34+
1. Start at node `0`, visit node `1`.
35+
2. From node `1`, visit node `3`, then backtrack to node `1`.
36+
3. From node `1`, visit node `4`, then backtrack to node `1`.
37+
4. Backtrack to node `0`, then visit node `2`.
38+
5. From node `2`, visit node `5`.
39+
40+
The DFS traversal would be: **0 → 1 → 3 → 4 → 2 → 5**
41+
42+
### **DFS Algorithm:**
43+
1. **Mark the starting node as visited.**
44+
2. **Recursively visit each unvisited neighbor** until all nodes are explored.
45+
46+
47+
### **Implementation of DFS**
48+
49+
Here’s a C++ implementation of DFS using both recursive and iterative approaches:
50+
51+
```cpp
52+
#include <iostream>
53+
#include <vector>
54+
#include <stack>
55+
using namespace std;
56+
57+
class Graph {
58+
public:
59+
vector<vector<int>> adjacencyList; // Adjacency list representation of the graph
60+
61+
// Constructor to initialize the graph with the given number of nodes
62+
Graph(int totalNodes) {
63+
adjacencyList.resize(totalNodes); // Resize the adjacency list to fit the total nodes
64+
}
65+
66+
// Method to add an edge to the graph
67+
void addEdge(int sourceNode, int destinationNode, bool isDirected = false) {
68+
adjacencyList[sourceNode].push_back(destinationNode); // Add destination node to source node's adjacency list
69+
if (!isDirected) {
70+
adjacencyList[destinationNode].push_back(sourceNode); // For undirected graph, add the reverse edge
71+
}
72+
}
73+
74+
// Recursive DFS implementation
75+
void dfsRecursive(int startNode, vector<bool>& visited) {
76+
visited[startNode] = true; // Mark the node as visited
77+
cout << startNode << " "; // Print the visited node
78+
79+
// Visit all the unvisited neighbors of the current node
80+
for (int neighbor : adjacencyList[startNode]) {
81+
if (!visited[neighbor]) {
82+
dfsRecursive(neighbor, visited); // Recursively visit the neighbor
83+
}
84+
}
85+
}
86+
87+
// Iterative DFS implementation using a stack
88+
void dfsIterative(int startNode) {
89+
vector<bool> visited(adjacencyList.size(), false); // Keep track of visited nodes
90+
stack<int> s; // Stack to manage the nodes
91+
92+
s.push(startNode); // Push the starting node into the stack
93+
94+
while (!s.empty()) {
95+
int currentNode = s.top(); // Get the top node from the stack
96+
s.pop(); // Remove it from the stack
97+
98+
if (!visited[currentNode]) {
99+
cout << currentNode << " "; // Print the current node
100+
visited[currentNode] = true; // Mark it as visited
101+
}
102+
103+
// Push all unvisited neighbors of the current node into the stack
104+
for (int neighbor : adjacencyList[currentNode]) {
105+
if (!visited[neighbor]) {
106+
s.push(neighbor);
107+
}
108+
}
109+
}
110+
}
111+
};
112+
113+
int main() {
114+
int totalNodes = 6; // Total number of nodes in the graph
115+
116+
Graph g(totalNodes); // Create a graph object
117+
118+
// Adding edges to the graph
119+
g.addEdge(0, 1);
120+
g.addEdge(0, 2);
121+
g.addEdge(1, 3);
122+
g.addEdge(1, 4);
123+
g.addEdge(2, 5);
124+
125+
cout << "DFS Traversal (Recursive): ";
126+
vector<bool> visited(totalNodes, false); // Visited array to track nodes
127+
g.dfsRecursive(0, visited); // Perform DFS starting from node 0
128+
cout << endl;
129+
130+
cout << "DFS Traversal (Iterative): ";
131+
g.dfsIterative(0); // Perform iterative DFS starting from node 0
132+
cout << endl;
133+
134+
return 0;
135+
}
136+
```
137+
### Source Code Explanation
138+
139+
Let's break down the DFS implementation in C++ line by line. This will include both the **recursive** and **iterative** approaches for DFS traversal.
140+
141+
```cpp
142+
#include <iostream>
143+
#include <vector>
144+
#include <stack>
145+
using namespace std;
146+
```
147+
148+
- **Line 1-3:** These are header files that include necessary functionalities:
149+
- `#include <iostream>`: Allows input and output operations (e.g., `cin`, `cout`).
150+
- `#include <vector>`: Enables the use of the `vector` container.
151+
- `#include <stack>`: Provides the `stack` container for the iterative DFS implementation.
152+
153+
154+
155+
```cpp
156+
class Graph {
157+
public:
158+
vector<vector<int>> adjacencyList; // Adjacency list representation of the graph
159+
```
160+
161+
- **Line 5-6:** Declares the `Graph` class:
162+
- `vector<vector<int>> adjacencyList`: This is a 2D vector (vector of vectors) used to store the adjacency list. Each node's neighbors will be stored in a list at the corresponding index.
163+
164+
165+
166+
```cpp
167+
Graph(int totalNodes) {
168+
adjacencyList.resize(totalNodes); // Resize the adjacency list to fit the total nodes
169+
}
170+
```
171+
172+
- **Line 8-9:** Constructor for the `Graph` class:
173+
- `Graph(int totalNodes)`: The constructor takes an integer (`totalNodes`) as the number of nodes in the graph.
174+
- `adjacencyList.resize(totalNodes)`: Resizes the `adjacencyList` to contain `totalNodes` number of empty lists. Each list will hold the neighboring nodes for each node.
175+
176+
177+
178+
```cpp
179+
void addEdge(int sourceNode, int destinationNode, bool isDirected = false) {
180+
adjacencyList[sourceNode].push_back(destinationNode); // Add destination node to source node's adjacency list
181+
if (!isDirected) {
182+
adjacencyList[destinationNode].push_back(sourceNode); // For undirected graph, add the reverse edge
183+
}
184+
}
185+
```
186+
187+
- **Line 11-14:** Adds an edge to the graph:
188+
- `addEdge(int sourceNode, int destinationNode, bool isDirected = false)`: This method adds an edge from `sourceNode` to `destinationNode`. The `isDirected` flag is `false` by default, indicating an undirected graph.
189+
- `adjacencyList[sourceNode].push_back(destinationNode)`: Adds `destinationNode` to the adjacency list of `sourceNode`.
190+
- If the graph is undirected (`!isDirected`), the reverse edge is added from `destinationNode` to `sourceNode`, ensuring bidirectionality.
191+
192+
193+
194+
```cpp
195+
void dfsRecursive(int startNode, vector<bool>& visited) {
196+
visited[startNode] = true; // Mark the node as visited
197+
cout << startNode << " "; // Print the visited node
198+
```
199+
200+
- **Line 16-18:** Recursive DFS method:
201+
- `dfsRecursive(int startNode, vector<bool>& visited)`: This method performs DFS traversal starting from `startNode`.
202+
- `visited[startNode] = true`: Marks the node as visited.
203+
- `cout << startNode << " "`: Prints the current node that is being visited.
204+
205+
206+
207+
```cpp
208+
for (int neighbor : adjacencyList[startNode]) {
209+
if (!visited[neighbor]) {
210+
dfsRecursive(neighbor, visited); // Recursively visit the neighbor
211+
}
212+
}
213+
}
214+
```
215+
216+
- **Line 20-23:** Visit all unvisited neighbors of the current node:
217+
- `for (int neighbor : adjacencyList[startNode])`: Loops through all neighbors of `startNode` from its adjacency list.
218+
- `if (!visited[neighbor])`: If the neighbor hasn't been visited, we recursively call `dfsRecursive(neighbor, visited)` to visit that neighbor.
219+
- This ensures that we visit all connected nodes in a depth-first manner.
220+
221+
222+
223+
```cpp
224+
void dfsIterative(int startNode) {
225+
vector<bool> visited(adjacencyList.size(), false); // Keep track of visited nodes
226+
stack<int> s; // Stack to manage the nodes
227+
```
228+
229+
- **Line 25-27:** Iterative DFS method:
230+
- `vector<bool> visited(adjacencyList.size(), false)`: Creates a `visited` vector initialized to `false` for all nodes, which tracks whether each node has been visited.
231+
- `stack<int> s`: Initializes a stack `s` to help manage the nodes as we explore the graph iteratively.
232+
233+
234+
235+
```cpp
236+
s.push(startNode); // Push the starting node into the stack
237+
```
238+
239+
- **Line 29:** Pushes the starting node into the stack, marking the beginning of our DFS traversal.
240+
241+
242+
243+
```cpp
244+
while (!s.empty()) {
245+
int currentNode = s.top(); // Get the top node from the stack
246+
s.pop(); // Remove it from the stack
247+
```
248+
249+
- **Line 31-33:** The main iterative DFS loop:
250+
- `while (!s.empty())`: Continues the loop until the stack is empty.
251+
- `int currentNode = s.top()`: Retrieves the top node from the stack.
252+
- `s.pop()`: Removes the top node from the stack after processing.
253+
254+
255+
256+
```cpp
257+
if (!visited[currentNode]) {
258+
cout << currentNode << " "; // Print the current node
259+
visited[currentNode] = true; // Mark it as visited
260+
}
261+
```
262+
263+
- **Line 35-37:** Process the current node:
264+
- `if (!visited[currentNode])`: Checks if the current node has been visited. If not, we process it.
265+
- `cout << currentNode << " "`: Prints the current node.
266+
- `visited[currentNode] = true`: Marks the current node as visited to prevent revisiting it.
267+
268+
269+
270+
```cpp
271+
for (int neighbor : adjacencyList[currentNode]) {
272+
if (!visited[neighbor]) {
273+
s.push(neighbor); // Push all unvisited neighbors of the current node into the stack
274+
}
275+
}
276+
}
277+
}
278+
```
279+
280+
- **Line 39-42:** Explore the neighbors:
281+
- `for (int neighbor : adjacencyList[currentNode])`: Loops through all neighbors of the current node.
282+
- `if (!visited[neighbor])`: If a neighbor has not been visited, it is pushed onto the stack for future processing.
283+
284+
285+
286+
```cpp
287+
int main() {
288+
int totalNodes = 6; // Total number of nodes in the graph
289+
```
290+
291+
- **Line 44:** Initializes the number of nodes in the graph to `6`.
292+
293+
294+
295+
```cpp
296+
Graph g(totalNodes); // Create a graph object
297+
```
298+
299+
- **Line 46:** Creates a `Graph` object `g` with `6` nodes. This initializes the graph and the adjacency list.
300+
301+
302+
303+
```cpp
304+
g.addEdge(0, 1);
305+
g.addEdge(0, 2);
306+
g.addEdge(1, 3);
307+
g.addEdge(1, 4);
308+
g.addEdge(2, 5);
309+
```
310+
311+
- **Line 48-52:** Adds edges to the graph:
312+
- These calls add edges between nodes, ensuring the graph has the desired structure:
313+
- `0 → 1`, `0 → 2`, `1 → 3`, `1 → 4`, `2 → 5`.
314+
315+
316+
317+
```cpp
318+
cout << "DFS Traversal (Recursive): ";
319+
vector<bool> visited(totalNodes, false); // Visited array to track nodes
320+
g.dfsRecursive(0, visited); // Perform DFS starting from node 0
321+
cout << endl;
322+
```
323+
324+
- **Line 54-57:** Performs recursive DFS traversal:
325+
- Initializes the `visited` array with `false`.
326+
- Calls `dfsRecursive(0, visited)` to start DFS from node `0`.
327+
- Prints the result of the recursive DFS traversal.
328+
329+
330+
331+
```cpp
332+
cout << "DFS Traversal (Iterative): ";
333+
g.dfsIterative(0); // Perform iterative DFS starting from node 0
334+
cout << endl;
335+
```
336+
337+
- **Line 59-61:** Performs iterative DFS traversal:
338+
- Calls `dfsIterative(0)` to start DFS from node `0` using an explicit stack.
339+
- Prints the result of the iterative DFS traversal.
340+
341+
342+
343+
```cpp
344+
return 0;
345+
}
346+
```
347+
348+
- **Line 63:** Exits the program.
349+
350+
351+
352+
### **Output Example:**
353+
354+
```
355+
DFS Traversal (Recursive): 0 1 3 4 2 5
356+
DFS Traversal (Iterative): 0 1 3 4 2 5
357+
```
358+
359+
Both the recursive and iterative DFS traverse the graph starting from node `0` and visit all nodes in depth-first order.
360+
361+
362+
363+
### **Time and Space Complexity:**
364+
365+
#### **Time Complexity:**
366+
- **DFS (Recursive and Iterative):**
367+
- **O(V + E)**, where:
368+
- `V` is the number of vertices (nodes).
369+
- `E` is the number of edges.
370+
- **Explanation:**
371+
- Each vertex is processed once (O(V)).
372+
- Each edge is explored once (O(E)).
373+
374+
#### **Space Complexity:**
375+
- **DFS (Recursive):**
376+
- **O(V)** due to the recursion stack and the visited array.
377+
- **DFS (Iterative):**
378+
- **O(V)** for the visited array and the stack used to manage the traversal.
379+
380+
### **Conclusion:**
381+
DFS is a versatile traversal technique
382+
383+
for graph traversal, and both recursive and iterative methods offer efficient ways to explore graphs.

0 commit comments

Comments
 (0)