Graph DFS & BFS Traversal
Memory Complexity
For a tree like structure, for the purpose of understanding
For a balanced tree with n nodes, there will n/2 leaf nodes due to which the memory complexity of BFS will on O(n)
For DFS, the max memory will the height of the tree which in logN, and that is why the memory complexity is O(logN)
Create Graph from List of Edges List
if edges array edges = [[0,1],[1,2],[2,0]] is given, first create a graph before applying DFS or BFS
If graph consists of integer nodes beginning 0 or 1, simply use an array and take advantage of the indices of the list
Visited Array
Can use a set of type node HashSet<Node> for both 1D and 2D arrays
- A
HashSet<Node> visitedcan be used used to keep track of visited node- use
visited.add(r+""+c)for 2D grids - use
visited.contains(r+""+c)
- use
Can simply use boolean array
// For 1D Array
boolean visited[] = new boolean[V];//Initialized with False
for(Integer i : adj.get(currentVertex)){
//If neighbor is not visited, add in the Queue or recurse for DFS and make it visited
if(!visited[i]){
stack.push(i);
//q.add(i);
visited[i] = true;
}
//For grid
int[][] visit = new int[ROWS][COLS];
If you are allowed to change the grid, the simple change the value to a negative scenario
Navigating along the grid
I find it a bit complicated
Use the direction array with ints
Depth First Search (DFS)
- Traverse all paths between any two vertices in a “graph”.
- Traversing all Vertices
- Traversing all paths between two vertices
Recursion
AdjacencyList
Grid DFS
Tests With Stack (Prefer DFS Recursion instead)
In place of the Stack if a queue is used, then the same algo becomes BFS
Recursive backtracking - DFS
Base case for recursion
//Base cases
if(//Covering the 4 corners
r < 0 || c < 0 //Math.min(r,c) < 0 //Left & Upper bound
|| r == grid.length //right bound
|| c == grid[0].length //lower bound
// Blockers as per the problem
|| grid[r][c] == 1 //Blocked
//Visited check
|| visited.contains(r+""+c) //Already Visited
){
return ...;//Depends if count is needed or a void is returned
}
Terminating conditions, if there is one fixed condition.
//Terminating condition, when the rightmost Bottom corner is reached
if(r == grid.length-1 && c == grid[0].length-1){
return 1;
}
If all possibilities are to be searched
int count = 0;
HashSet<String> visited = new HashSet<>();
for(int i = 0; i < grid.length; i++){
for(int j = 0; j < grid[0].length; j++){
if(grid[i][j] == '1' && !visited.contains(i+""+j)){
count++;
gridDFS(grid,i,j,visited);
}
}
}
The directions array comes handy with BFS as there are no recursive calls
private static final int[][] directions = {
{1, 0}, //Next Row → , r-1, Keeping Column constant
{-1, 0}, //Previous Row ←, r+1
{0, 1}, //Column above ↑, c+1, Keeping Row constant
{0, -1}}; //Column above ↓, c-1
//while performing dfs
for (int[] dir : directions) {
dfs(grid, r + dir[0], c + dir[1]);
}
Using Recursion
Pass the updated visited array so that the other calls have the updated set
//Recursive Backtracking
gridDFS(grid,r,c+1,visited);
gridDFS(grid,r,c-1,visited);
gridDFS(grid,r+1,c,visited);
gridDFS(grid,r-1,c,visited);
Complexity
Time Complexity: $ O(V+E) $
- 𝑉 represents the number of vertices, and
- 𝐸 represents the number of edges.
- We need to check every vertex and traverse through every edge in the graph.
Space Complexity: $ 𝑂(𝑉^2) $
- created stack or the recursive call stack can store up to V⋅V vertices
- worst case - each vertex has edges connecting to all other vertices.
Breadth First Search - BFS
-
Finding the shortest path between two vertices in a graph where all edges have equal and positive weights.
- Visits each node once
- Running time complexity O(V+E)
- Space complexity is not good, due to an extra Queue, which is why DFS is preferred
- Dijkstra’s Algo does a BFS if all the edge weight is equal to 1.
- In AI(Machine Learning) Robots can discover surroundings more easily from BFS than DFS
- Important in Maximum Flow - Edmonds-Karp Algorithm