Algorithm summary - depth-first traversal and breadth-first traversal

Depth First Search (DFS for short) and Breadth First Search (BFS for short) are two very important algorithms in graph theory, which are widely used in topological sorting, pathfinding (maze walking), search Engines, crawlers, etc.

1. Depth-first traversal
The idea of ​​depth-first traversal is to start from an unvisited vertex V of the graph, go all the way to the end along a road, and then return to the previous node from the node at the end of the road, and then start from another road Start to go to the end, and repeat this process recursively...until all vertices are traversed. Its characteristics are popular, that is, don't hit the south wall and don't look back. After walking one road, change another road and continue walking.
A tree is a special case of a graph (a graph without loops in Unicom is a tree). Next, let's look at how to traverse a tree with depth-first traversal.
1. Depth-first traversal process
insert image description here
(1). We start depth-first traversal from the root node 1. Its adjacent nodes are 2, 3, and 4. First traverse node 2, then traverse node 5 on the right side of 2, and then traverse 9. So far there are no nodes to traverse.
insert image description here

(2) A path in the above figure has been traversed to the end. At this time, roll back from the leaf node 9 to the previous node 5. Check whether there are nodes other than 9 in the next node 5. If you do not continue to roll back to 2, 2 also If there is no node other than 5, go back to 1, 1 has node 3 other than 2, so start from node 3 for depth-first traversal, as follows: (3), similarly start from 10 and go back
insert image description here
to 6, 6 does not For child nodes other than 10, go back up and find that 3 has child node 7 other than 6, so 7 will be traversed at this time.
insert image description here
(4) Backtracking from 7 to 3, 1, it is found that 1 still has node 4 that has not been traversed, so it traverses along 4, 8 at this time, thus completing the entire traversal process.
The traversal order of the complete nodes is as follows (represented by the blue numbers on the nodes):
insert image description here
I believe that it is not difficult for everyone to see the above traversal to find that this is the pre-order traversal of the tree. In fact, whether it is pre-order traversal or in-order traversal, Or post-order traversal, all belong to depth-first traversal.

So how to implement depth-first traversal, there are two forms of expression: recursive and non-recursive. Next, let's take a binary tree as an example to see how to implement depth-first traversal with recursion and non-recursion respectively.
2. Depth-first traversal implementation
(1). Recursive
recursive implementation is relatively simple. Since it is a pre-order traversal, we traverse the current node, left node, and right node in turn. For left and right nodes, traverse their left and right nodes in turn. Yes, continue to recurse accordingly until the leaf node (recursive termination condition), the code is as follows:

public class Solution {
    
     
    private static class Node {
    
     
        public int value;  // 节点值
        public Node left;  // 左节点  
        public Node right;  // 右节点
 
        public Node(int value, Node left, Node right) {
    
     
            this.value = value; 
            this.left = left; 
            this.right = right; 
        } 
    } 
 
    public static void dfs(Node treeNode) {
    
     
        if (treeNode == null) {
    
     
            return; 
        } 
        process(treeNode);  // 遍历节点
        dfs(treeNode.left);  // 遍历左节点
        dfs(treeNode.right);  // 遍历右节点
    } 
} 

Recursion is very expressive and easy to understand, but if the level is too deep, it is easy to cause stack overflow. So let's focus on the non-recursive implementation.
(2) Non-recursive.
Carefully observe the characteristics of depth-first traversal. For a binary tree, because it is a pre-order traversal (first traverse the current node, then traverse the left node, and then traverse the right node), so we have the following ideas:

For each node, first traverse the current node, then push the right node on the stack, and then push the left node (so that when popping the stack, the left node will be traversed first, which meets the requirements of depth-first traversal).
Pop the stack and get the node at the top of the stack. If the node is not empty, repeat step 1. If it is empty, end the traversal.
Let's take the following binary tree as an example to see how to implement DFS with a stack.
insert image description here
insert image description here

Use the stack to push the nodes to be traversed on the stack, and then check whether there are untraversed nodes in this node after popping the stack. If there are any nodes, push the stack, and if not, keep backtracking (popping the stack). With the idea, it is not difficult to write the following The depth-first traversal code of the binary tree implemented by the stack:

/** 
 * 使用栈来实现 dfs 
 * @param root 
 */ 
public static void dfsWithStack(Node root) {
    
     
    if (root == null) {
    
     
        return; 
    } 
 
    Stack<Node> stack = new Stack<>(); 
    // 先把根节点压栈 
    stack.push(root); 
    while (!stack.isEmpty()) {
    
     
        Node treeNode = stack.pop(); 
        // 遍历节点 
        process(treeNode) 
 
        // 先压右节点 
        if (treeNode.right != null) {
    
     
            stack.push(treeNode.right); 
        } 
 
        // 再压左节点 
        if (treeNode.left != null) {
    
     
            stack.push(treeNode.left); 
        } 
    } 
}

2. Depth-first traversal
Breadth-first traversal refers to starting from an untraversed node in the graph, first traversing the adjacent nodes of this node, and then traversing the adjacent nodes of each adjacent node in turn.

The breadth-first traversal animation of the tree mentioned above is as follows, and the value of each node is their traversal order. Therefore, breadth-first traversal is also called layer-order traversal, first traversing the first layer (node ​​1), then traversing the second layer (node ​​2, 3, 4), the third layer (5, 6, 7, 8), and the fourth layer (9, 10).
insert image description here

Depth-first traversal uses a stack, while breadth-first traversal is implemented using a queue. Let's take the following binary tree as an example to see how to use a queue to implement breadth-first traversal.
insert image description here
insert image description here
The code is implemented as follows:

/** 
 * 使用队列实现 bfs 
 * @param root 
 */ 
private static void bfs(Node root) {
    
     
    if (root == null) {
    
     
        return; 
    } 
    Queue<Node> stack = new LinkedList<>(); 
    stack.add(root); 
 
    while (!stack.isEmpty()) {
    
     
        Node node = stack.poll(); 
        System.out.println("value = " + node.value); 
        Node left = node.left; 
        if (left != null) {
    
     
            stack.add(left); 
        } 
        Node right = node.right; 
        if (right != null) {
    
     
            stack.add(right); 
        } 
    } 
} 

If you want to further understand DFS and BFS through actual combat, you can see the following topic of LeetCode, which is a typical DFS and BFS topic.
leetcode 104, 111: Given a binary tree, find its max/min depth.

Guess you like

Origin blog.csdn.net/hhhlizhao/article/details/130094209