Binary tree traversal [preorder, inorder, postorder, level] (detailed explanation)

Summary

The traversal of the binary tree is also a very common question, and the most commonly used traversal is also the four ways mentioned in the title.
Pre-order, in-order and post-order can be done in a recursive and iterative way, which is also a depth-first idea, and the recursive and iterative methods will be written later.
Hierarchical traversal mainly uses the data structure of the queue to traverse the binary tree layer by layer, which is a breadth-first idea.
Now let's write the traversal methods for each.

1. Inorder traversal

Let me first talk about the way of in-order traversal. For each node of the binary tree, starting from the root node, the left child node of the current node must be traversed first, then the current node, and then the right child node of the current node.

Simply put, it is left subtree -> root node -> right subtree .

The recursive method is very simple. We won't go into details here, but just upload the code.

const logSort1 = function (arr, node) {
    
    
  if (node != null) {
    
    
    logSort1(arr, node.left)
    arr.push(node.val)
    logSort1(arr, node.right)
  }
}

If we want to use an iterative method, we must have a stack, because the essence of recursion actually maintains an internal stack, so we also need to create a stack ourselves if we want to use a loop.

first step:

Since the way of traversal is the left subtree first, we can push the stack from the root node to each left child node below.
insert image description here

Step two:

When popping the stack, we can put the popped node into the result set, then get the right child node of the popped node, and repeat the first step with the right child node as the root node. The figure below shows the situation after the node 2 is popped out of the stack, and the subsequent situation can be pushed forward by yourself.
insert image description here

third step:

Until the stack is empty and the last node popped has no right child , we can say that the traversal is over.

2. Preorder traversal

The method of preorder traversal, for all nodes of the binary tree, traverses the current node first, then traverses the left child node of the current node, and finally traverses the right child node of the current node.

In summary: root node -> left subtree -> right subtree

Similarly, let's not talk about the recursive method, just go directly to the code.

const logSort3 = function (arr, node) {
    
    
  if (node != null) {
    
    
    arr.push(node.val);
    logSort3(arr, node.left);
    logSort3(arr, node.right);
  }
}

Let's mainly talk about the way of iteration. Similarly, as long as we use loops without recursion, then we must first maintain our own stack.

first step:

Since it is the root node first, and then the left child node, we still push the stack from the root node to each left child node in turn. However, remember that the inorder is to put the result set when popping the stack, but this time we root The priority of the node is greater than that of the left child node, so when we push the stack, we must put the node into the result set.
insert image description here

Step two:

Pop out, this time the node we popped out of the stack will not be included in the result set, but the right child node of our popped node must repeat the first step. The following figure shows the situation of 2 nodes popping out of the stack.
insert image description here
third step:

We cannot consider the traversal to be complete until the stack is empty and the last node popped does not contain a right child.

const logSort4 = function (root) {
    
    
  let stack = [], res = [];
  while (stack.length > 0 || root) {
    
    
    while (root != null) {
    
    
      res.push(root.val)
      stack.push(root)
      root = root.left;
    }
    let node = stack.pop();
    root = node.right
  }
  return res;
}

3. Post-order traversal

The case of post-order traversal is that, for all nodes of the binary tree, first traverse the left child node of the current node, then the current node, and finally the right child node of the current node.
In summary: left subtree -> root node -> right subtree

The recursive method is directly on the code, and the focus is still on the iterative method:

const logSort5 = function (arr, node) {
    
    
  if (node != null) {
    
    
    logSort5(arr, node.left);
    logSort5(arr, node.right);
    arr.push(node.val);
  }
}

With the explanation of the first two traversals, we must know that we need to maintain a stack first, so how do we use loops to solve the post-order traversal situation?

first step:

We must remember the priority, the priority of this traversal method is left > right > root.
Since the left subtree has the highest priority, we still push from the root node to each left child node. Since the root node has the lowest priority, we cannot perform the operation of adding the result set when pushing to the stack.
insert image description here

Step two:

The situation of popping the stack is a bit complicated, let's think about it:

If the stack node does not have a right child node (the situation of the left child node is not considered, because the left child node is pushed into the stack), or the right child node has been entered into the result set . Then, can it explain that the stack node has become the highest priority (or a root node with no left and right nodes), at this time we can put the node into the result set.

If the stack node has a right child node , it means that the priority of the stack node must not be higher than the right child node, then we can only prohibit the pop node from the stack , and put the right child node with a higher priority than it into the stack. And repeat the first step with this right child node .

It's a little difficult to understand here, but as long as you remember the order of priority and remember that I pushed the stack in the order of the left child node, you can figure it out.

The picture below shows the situation where the 2 nodes are about to pop out of the stack.
insert image description here

third step:

Until the stack is empty and the popped node has no right child, we can say that the traversal is over.

const logSort6 = function (root) {
    
    
  let stack = [], res = [], pre = null
  while (stack.length > 0 || root) {
    
    
    while (root != null) {
    
    
      stack.push(root)
      root = root.left;
    }
    root = stack.pop();
    if (root.right === null || pre === root.right) {
    
    
      res.push(root.val)
      pre = root;
      root = null
    } else {
    
    
      stack.push(root)
      root = root.right
    }

  }
  return res
}

4. Hierarchical traversal

Regarding hierarchical traversal, it is also implemented through the data structure of the queue, first in first out, and in a breadth-first manner.

Step 1: We first put the root node into the queue.
insert image description here

Step 2: We dequeue all the nodes in the queue and enter the result set, and when each node dequeues, the left and right child nodes of the node are put into the queue.

insert image description here
Step 3: Repeat step 2 until the queue is empty and we can say that the traversal is over.

Note here that our hierarchical traversal can record the number of layers.

const logSort7 = function (root) {
    
    
  let queue = [root], res = [];
  while (queue.length > 0) {
    
    
    let len = queue.length;
    let arr = [];
    for (var i = 0; i < len; i++) {
    
    
      let node = queue.shift();
      arr.push(node.val)
      if (node.left) {
    
    
        queue.push(node.left)
      }
      if (node.right) {
    
    
        queue.push(node.right)
      }
    }
    res.push(arr)
  }
  return res
}

Guess you like

Origin blog.csdn.net/weixin_46726346/article/details/120324098