[Binary tree] ACM players take you to play the level traversal of binary tree (recursive + non-recursive)

Get into the habit of writing together! This is the 9th day of my participation in the "Nuggets Daily New Plan · April Update Challenge", click to view the details of the event .


Hello everyone, I'm the handsome guy on the 18th floor.

As I said before, the implementation of binary tree traversal has historically been a high-frequency interview question.

The binary tree traversals mentioned here are generally: pre-order traversal, in-order traversal, post-order traversal and hierarchical traversal.

There are two ways to implement pre-in-post-order traversal of binary trees: recursive and non-recursive . I have already introduced it in the previous two articles:

ACM players take you to play the binary tree pre-middle-post-order traversal (recursive version)

ACM players take you to play binary tree pre-middle-post-order traversal (non-recursive version)

Students who haven't seen it can watch it.

Today, let's talk about the remaining level traversal.

Hierarchical traversal means the surface meaning, the traversal of layers, the traversal of the same layer is traversed one by one from left to right.

a74a0cd2c1a6b8b1b50bb0bba5e83fc

Like the above binary tree, its hierarchical traversal order is: ABCDEFGHIJ.

Regarding the hierarchical traversal of a binary tree, there are also two implementations: recursive and non-recursive, but the hierarchical traversal is somewhat different from the previous pre-middle-post-order traversal because of its traversal properties .

8f1cc9e601093c2882f112fd240c146

Below I use a LeetCode question to explain the recursive and non-recursive methods of binary tree level traversal in detail.

Hierarchical Traversal of Binary Tree

LeetCode 102: Hierarchical Traversal of Binary Trees

Given a binary tree, please return the node value obtained by traversing it hierarchically.

Example

81ea33e3736d7ec202f370c6afa5043

recursive version

Parse

Binary tree level traversal uses the recursive method, which is a bit anti-level traversal to be honest.

Hierarchical traversal is to traverse layer by layer, and we all know that recursion is a feature that solves the problem to the end and then solves other problems, that is, depth, as follows:

b2ef120339fcc5905ebf08c7ae0c567

Now that you're ready to use recursion, post the recursion two-step:

(1) Find duplicate subproblems.

Hierarchical traversal is the traversal of nodes at each level from left to right, so we can traverse the left subtree first, and then the right subtree .

0c7f39e4fa7d8f2049f320f60529d94

It might be clearer to replace it with the following:

c5b690bd33501ac6845ca5325a032ce

遍历的顺序是:3 -> 9 -> 3 -> 20 -> 15 -> 20 -> 7。

需要注意的是,在遍历左子树或者右子树的时候,涉及到向上或者向下遍历,为了让递归的过程中的同一层的节点放在同一个列表中,在递归时要记录深度 depth

同时,每次遍历到一个新的 depth,结果数组中没有对应的 depth 的列表时,在结果数组中创建一个新的列表保存该 depth 的节点

# 若当前行对应的列表不存在,加一个空列表
if len(res) < depth:
    res.append([])
复制代码

(2) 确定终止条件。

对于二叉树的遍历来说,想终止,即没东西遍历了,没东西遍历自然就停下来了。

即最下面一层的左右节点都为空了。

if root == None:
    return []
复制代码

还是那句话,最重要的两点确定完了,那代码也就出来了。

二叉树层次遍历递归版,由于每个节点都被遍历到,所以时间复杂度为 O(n)

此外在递归过程中调用了额外的栈空间,维护了一个 res 的结果数组,所以空间复杂度为 O(n)

代码实现

Python 代码实现

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
​
    def level(self, root: TreeNode, depth, res):
        if root == None:
            return []
​
        # 若当前行对应的列表不存在,加一个空列表
        if len(res) < depth:
            res.append([])
​
        # 将当前节点的值加入当前行的 res 中
        res[depth - 1].append(root.val)
​
        # 递归处理左子树
        if root.left:
            self.level(root.left, depth + 1, res)
        # 递归处理右子树
        if root.right:
            self.level(root.right, depth + 1, res)
​
​
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
​
        res = []
        self.level(root, 1, res)
​
        return res
复制代码

Java 代码实现

class Solution {
​
    void level(TreeNode root, int index, List<List<Integer>> res) {
    if(res.size() < index) {
      res.add(new ArrayList<Integer>());
    }
​
    res.get(index-1).add(root.val);
​
    if(root.left != null) {
      level(root.left, index+1, res);
    }
​
    if(root.right != null) {
      level(root.right, index+1, res);
    }
  }
​
  public List<List<Integer>> levelOrder(TreeNode root) {
    if(root == null) {
      return new ArrayList<List<Integer>>();
    }
​
    List<List<Integer>> res = new ArrayList<List<Integer>>();
    level(root, 1, res);
    return res;
  }
}
复制代码

非递归版(迭代)

解析

非递归版的层次遍历和之前讲的非递归版的前中后序遍历,还是不同的

非递归版的前中后续遍历,是将递归中用的栈光明正大的模拟出来。

非递归版的层次遍历我们用的则是队列,这是由于层次遍历的属性非常契合队列的特点。

队列是一种先进先出(First in First Out)的数据结构,简称 FIFO

如果不太了解队列的,可以看下面这篇文章:

ACM 选手带你玩转栈和队列

思路很简单:

Use a queue to save all nodes in each layer, dequeue all nodes in the queue, and then put the respective child nodes of these outgoing nodes into the queue .

This completes the traversal of each layer.

7743f151dc650c9f546bb17e854464d

# 存储当前层的孩子节点列表
childNodes = []
​
for node in queue:
    # 若节点存在左孩子,入队
    if node.left:
        childNodes.append(node.left)
    # 若节点存在右孩子,入队
    if node.right:
        childNodes.append(node.right)
# 更新队列为下一层的节点,继续遍历
queue = childNodes
​
复制代码

The non-recursive version of the hierarchical traversal of the binary tree, each node enters and leaves the queue once, so the time complexity is O(n) .

In addition, a queue and a result array are additionally maintained, so the space complexity is O(n) .

Code

Python code implementation

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
​
class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
​
        if root == None:
            return []
​
        res = []
        queue = [root]
​
        while queue:
            res.append([node.val for node in queue])
            # 存储当前层的孩子节点列表
            childNodes = []
​
            for node in queue:
                # 若节点存在左孩子,入队
                if node.left:
                    childNodes.append(node.left)
                # 若节点存在右孩子,入队
                if node.right:
                    childNodes.append(node.right)
            # 更新队列为下一层的节点,继续遍历
            queue = childNodes
        return res
复制代码

Java code implementation

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        if (root == null) {
            return res;
        }
​
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.offer(root);
​
        while (!queue.isEmpty()) {
            List<Integer> curlevel = new ArrayList<Integer>();
            int curlevelLen = queue.size();
            for (int i = 1; i <= curlevelLen; ++i) {
                TreeNode node = queue.poll();
                curlevel.add(node.val);
​
                if (node.left != null) {
                    queue.offer(node.left);
                }
​
                if (node.right != null) {
                    queue.offer(node.right);
                }
            }
            res.add(curlevel);
        }
​
        return res;
    }
}
复制代码

At this point, the level traversal of binary tree (recursion + non-recursion) is finished. Have you learned anything?

So far, I have introduced the four traversal of binary tree completely. I hope you can read it carefully and give me a thumbs up.

f1d6683bca37ec2d72a132f61b43dca

I'm handsome, see you next time~

Guess you like

Origin juejin.im/post/7084786042867286023