首页 重启DAY9 二叉树的层序遍历
文章
取消

重启DAY9 二叉树的层序遍历

跟着灵茶山艾府大佬学习算法思路的day9,目标是在一个月内掌握hot100

b站链接如下:

二叉树的层序遍历【基础算法精讲 13】

二叉树的层序遍历 题目描述:

给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。

示例 1:

img

1
2
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]

示例 2:

1
2
输入:root = [1]
输出:[[1]]

示例 3:

1
2
输入:root = []
输出:[]

思路:

最基本的思路其实没啥好说的,就是用队列逐步入队下层节点就行,但在具体的实现上有些说道。我们可以考虑在一个数组上,在将遍历到的节点放入一个临时数组以记录到答案中的同时,以队列方式一直推入在遍历的节点的左右节点来保证循环,但这种方法使用的shift()会一直带来数组的队首元素删除,效率上是不佳的。而灵神的做法通过cur和nxt数组来分层,在处理cur当前层的同时用nxt收集下一层,就避免了shift()的o(n)尴尬。

代码:

shift()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var levelOrder = function(root) {
    if (!root) return [];
    
    const result = [];
    const queue = [root];
    
    while (queue.length) {
        const levelSize = queue.length;
        const currentLevel = []; // 用以临时记录当前遍历的层的所有节点
        
        // 根据queue当前数量,处理当前层的所有节点
        for (let i = 0; i < levelSize; i++) {
            const node = queue.shift();
            currentLevel.push(node.val);
            
            // 将子节点加入队列
            if (node.left) queue.push(node.left);
            if (node.right) queue.push(node.right);
        }
        
        result.push(currentLevel);
    }
    
    return result;
};

灵神的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var levelOrder = function(root) {
    if (root === null) {
        return [];
    }
    const ans = [];
    let cur = [root];
    while (cur.length) {
        const nxt = [];
        const vals = [];
        for (const node of cur) {
            vals.push(node.val);
            if (node.left)  nxt.push(node.left);
            if (node.right) nxt.push(node.right);
        }
        cur = nxt;
        ans.push(vals);
    }
    return ans;
};

总结:

其实shift的劣势是显而易见的,不需要太多分析,认真分析的原因是我一开始是这么做的,现在再看显得有些笨比。

不过值得一提的是,在力扣的小规模测试数据上,shift方法的效果甚至还能优一些些,一方面是数据量太少,没进入shift的劣势拐点,另一方面每次都新建一个nxt数组的空间创建开销也是比较明显的。

上为双数组,下为shift。

image-20260503010404005

二叉树的锯齿形层序遍历 题目描述:

给你二叉树的根节点 root ,返回其节点值的 锯齿形层序遍历 。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。

示例 1:

img

1
2
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[20,9],[15,7]]

示例 2:

1
2
输入:root = [1]
输出:[[1]]

示例 3:

1
2
输入:root = []
输出:[]

思路:

思路还是之前的思路,只是在偶数层时我们翻转一下,因为需要返回的数组本身就是二维数组,是带有目前已遍历的层的信息的,我们自然也能够知道接下来的一层是奇数层还是偶数层,所以根据ans的长度做翻转判断就好。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var zigzagLevelOrder = function(root) {
    if (!root) return [];
    
    const ans = [];
    let cur = [root];
    
    while (cur.length) {
        const nxt = [];
        const vals = [];
        
        for (const node of cur) {
            vals.push(node.val);
            if (node.left) nxt.push(node.left);
            if (node.right) nxt.push(node.right);
        }
        
        // ans.length % 2:0 意味着要推入的本层为奇数层,不反转
        // 1 意味着要推入的是偶数层,反转
        ans.push(ans.length % 2 ? vals.reverse() : vals);
        cur = nxt;
    }
    
    return ans;
};

总结:

沿用灵神的层序设计思路,很简单的一个延伸。

找树左下角的值 题目描述:

给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。

假设二叉树中至少有一个节点。

示例 1:

img

1
2
输入: root = [2,1,3]
输出: 1

示例 2:

img

1
2
输入: [1,2,3,4,null,5,6,null,null,7]
输出: 7

思路:

求最底层最左边有两个方法,一个是求层序遍历完成后,取出最后一层的第一个节点(可以直接层序完后,将return的结果改为[ans.length-1][0],但这样的效率很低,应该减少节点操作数量);一个是将原先从左到右的层序遍历改为从右到左,这样所求就是最后一个节点。

代码:

1
2
3
4
5
6
7
8
9
10
11
var findBottomLeftValue = function(root) {
    const cur = [root];
    let node = null;
    while (cur.length) { // 空数组 [] 在 JS 中是真值,不能cur
        node = cur.shift();
        if (node.right) cur.push(node.right); // 先右
        if (node.left) cur.push(node.left); // 后左
    }
 
    return node.val;
};

总结:

因为只需要求从右到左最后一个节点,所以对于结果的存储可以简洁一些,不需要存下整个遍历数组。

本文由作者按照 CC BY 4.0 进行授权

阅读DAY12 JavaScript高级程序设计 8章下 对象、类与面向对象编程

重启DAY9 回溯算法套路①子集型回溯