Sequence traversal of Leetcode No.102 binary tree

Title Description

To give you a binary tree, please return the node values obtained by traversing in sequence. (that is, access all nodes from left to right layer by layer).

Example: Binary tree: [3,9,20,null,null,15,7],

3 / \ 9 20 / \ 15 7 Return its sequence traversal result:

[ [3], [9,20], [15,7] ]

Problem solving ideas

DFS (depth first search) and BFS (breadth first search) are like twin brothers. When you mention one, you always think of another. However, in practice, we use DFS much more than BFS. So, is BFS useless?

If we only use DFS/BFS to traverse all nodes in a tree and a graph, there is no difference in the capabilities of DFS and BFS. Of course, we prefer DFS traversal with more write convenience and lower space complexity. However, DFS can only use BFS traversal for some usage scenarios, such as "sequence traversal" in this topic.

Let's first look at the code comparison between DFS traversal and BFS traversal on a binary tree.

DFS traversal uses recursion:

void dfs(TreeNode root) {
    if (root == null) {
        return;
    }
    dfs(root.left);
    dfs(root.right);
}

BFS traversal uses queue data structure:

void bfs(TreeNode root) {
    Queue<TreeNode> queue = new ArrayDeque<>();
    queue.add(root);
    while (!queue.isEmpty()) {
        TreeNode node = queue.poll(); // Java pop writing (poll)
        if (node.left != null) {
            queue.add(node.left);
        }
        if (node.right != null) {
            queue.add(node.right);
        }
    }
}

Just comparing the two pieces of code, the most intuitive feeling is that the DFS traversal code is too concise than BFS! This is because the recursive method implicitly uses the system stack, and we don't need to maintain a data structure ourselves. If you simply traverse the binary tree, DFS is obviously a more convenient choice.

Although DFS and BFS traverse all nodes of the binary tree, they traverse nodes in different order.

This traversal order is also the fundamental reason why BFS can be used to solve the "sequence traversal" problem.

What is sequence traversal? In short, sequence traversal is to layer the binary tree, and then traverse each layer from left to right:

At first glance, this traversal order is the same as BFS. We can directly use BFS to obtain the sequence traversal result. However, the input results required for sequence traversal and BFS are different. Sequence traversal requires us to distinguish each layer, that is, to return a two-dimensional array. The traversal result of BFS is a one-dimensional array, which can not distinguish each layer.

So, how to layer the results of BFS traversal? Let's first observe the process of nodes entering and leaving the queue during BFS traversal:

Intercept a time during BFS traversal:

You can see that the nodes in the queue are 3, 4 and 5, which are from layer 1 and layer 2 respectively. At this time, before the nodes of layer 1 are finished, the nodes of layer 2 come in, and the nodes of the two layers are close together in the queue. We can't distinguish which layer the nodes in the queue come from.

Therefore, we need to modify the code slightly. Before traversing each layer, first record the number of nodes in the queue n (that is, the number of nodes in this layer), and then process the N nodes in this layer at one go.

// Sequence traversal of binary tree
void bfs(TreeNode root) {
    Queue<TreeNode> queue = new ArrayDeque<>();
    queue.add(root);
    while (!queue.isEmpty()) {
        int n = queue.size();
        for (int i = 0; i < n; i++) { 
            // Variable i has no practical meaning, just to cycle n times
            TreeNode node = queue.poll();
            if (node.left != null) {
                queue.add(node.left);
            }
            if (node.right != null) {
                queue.add(node.right);
            }
        }
    }
}

In this way, we transform BFS traversal into sequence traversal. In the process of traversal, the process of nodes entering and leaving the queue is as follows:

It can be seen that in each round of the while loop, all nodes of the current layer are out of the queue, and then all nodes of the next layer are in the queue, so as to realize sequence traversal.

The final solution code is:

public List<List<Integer>> levelOrder(TreeNode root) {
    List<List<Integer>> res = new ArrayList<>();

    Queue<TreeNode> queue = new ArrayDeque<>();
    if (root != null) {
        queue.add(root);
    }
    while (!queue.isEmpty()) {
        int n = queue.size();
        List<Integer> level = new ArrayList<>();
        for (int i = 0; i < n; i++) { 
            TreeNode node = queue.poll();
            level.add(node.val);
            if (node.left != null) {
                queue.add(node.left);
            }
            if (node.right != null) {
                queue.add(node.right);
            }
        }
        res.add(level);
    }

    return res;
}

Complexity analysis

Note that the number of all nodes on the tree is n.

Time complexity: each point enters and exits the team once, so the progressive time complexity is O(n). Space complexity: the number of elements in the queue does not exceed n, so the progressive space complexity is O(n).

Posted on Mon, 29 Nov 2021 06:18:48 -0500 by m2babaey