leetcode 102二叉树的层序遍历

cpp 复制代码
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        if (root == nullptr) {
            return {};
        }

        vector<vector<int>> ans;
        queue<TreeNode*> q;

        q.push(root);

        // 外层 while:一层一层处理
        while (q.empty() == false) {

            // 当前这一层节点数量
            int n = q.size();

            // 每一层都重新创建一个 layer
            vector<int> layer;

            // 内层 while:处理当前这一层的 n 个节点
            while (n != 0) {

                // 取出队头节点
                TreeNode* node = q.front();
                q.pop();

                // 当前节点加入当前层
                layer.push_back(node->val);

                // 当前层还剩一个节点少处理
                n--;

                // 把下一层节点加入队列
                if (node->left != nullptr) {
                    q.push(node->left);
                }

                if (node->right != nullptr) {
                    q.push(node->right);
                }
            }

            // 当前层处理完毕
            ans.push_back(layer);
        }

        return ans;
    }
};

这段代码的整体思路就是:

用一个队列 queue 做层序遍历,每次先记住"当前层有多少个节点",然后把这一层的节点全部取出来,同时把它们的左右孩子放进队列。这样下一轮处理的就是下一层。

假设二叉树是:

复制代码
        3
       / \
      9   20
         /  \
        15   7

最终希望得到:

复制代码
[
    [3],
    [9, 20],
    [15, 7]
]

先看代码:

复制代码
vector<vector<int>> ans;
queue<TreeNode*> q;
q.push(root);

这里:

复制代码
ans

负责保存最终答案。

复制代码
q

负责保存"接下来要处理的节点"。

最开始:

复制代码
q = [3]
ans = []

然后进入外层循环:

复制代码
while(q.empty() == false)

意思是:

只要队列里面还有节点,就继续处理。

第一次进入外层循环时:

复制代码
q = [3]

创建当前层:

复制代码
vector<int> layer;

所以:

复制代码
layer = []

然后:

复制代码
int n = q.size();

此时:

复制代码
n = 1

这个 n 非常关键。

它表示:

当前这一层有 1 个节点。

所以接下来:

复制代码
while(n != 0)

就是把当前这一层的 1 个节点全部处理掉。


第一层

当前:

复制代码
q = [3]
n = 1
layer = []

执行:

复制代码
TreeNode* node = q.front();

那么:

复制代码
node -> 3

然后:

复制代码
layer.push_back(node->val);

得到:

复制代码
layer = [3]

然后:

复制代码
q.pop();

3 从队列删除:

复制代码
q = []

然后:

复制代码
n--;

得到:

复制代码
n = 0

接着检查 3 的左右孩子:

复制代码
if(node->left != nullptr){
    q.push(node->left);
}

if(node->right != nullptr){
    q.push(node->right);
}

3 的左右孩子是:

复制代码
9
20

所以放入队列:

复制代码
q = [9, 20]

注意这里非常重要。

虽然现在:

复制代码
q = [9,20]

但是:

复制代码
n = 0

所以:

复制代码
while(n != 0)

结束。

这意味着:

9、20 虽然已经进入队列,但是它们属于下一层,所以这一轮先不处理。

然后:

复制代码
ans.push_back(layer);

得到:

复制代码
ans = [
    [3]
]

第二层

又回到:

复制代码
while(q.empty() == false)

现在:

复制代码
q = [9,20]

队列不为空,所以继续。

重新创建:

复制代码
vector<int> layer;

所以:

复制代码
layer = []

然后:

复制代码
int n = q.size();

现在:

复制代码
n = 2

代表:

当前第二层有两个节点:9 和 20。

开始内层循环。

先处理 9

复制代码
TreeNode* node = q.front();

得到:

复制代码
node = 9

加入:

复制代码
layer = [9]

弹出:

复制代码
q = [20]

然后:

复制代码
n = 1

9 没有左右孩子,所以没有新节点入队。


接着因为:

复制代码
n = 1

继续内层循环。

现在:

复制代码
node = q.front();

得到:

复制代码
node = 20

加入:

复制代码
layer = [9,20]

弹出:

复制代码
q = []

然后:

复制代码
n = 0

但是 20 有两个孩子:

复制代码
15
7

所以:

复制代码
q = [15,7]

内层循环结束,因为:

复制代码
n = 0

然后:

复制代码
ans.push_back(layer);

得到:

复制代码
ans = [
    [3],
    [9,20]
]

第三层

现在:

复制代码
q = [15,7]

进入下一轮。

复制代码
int n = q.size();

所以:

复制代码
n = 2

处理 15

复制代码
layer = [15]
q = [7]
n = 1

处理 7

复制代码
layer = [15,7]
q = []
n = 0

两个节点都没有孩子。

所以:

复制代码
ans = [
    [3],
    [9,20],
    [15,7]
]

此时:

复制代码
q = []

所以外层:

复制代码
while(q.empty() == false)

不成立,整个遍历结束。


你可以把整个执行过程压缩成这样:

复制代码
开始:
q = [3]

第一层:
n = 1
处理 3
q = [9,20]

ans = [[3]]

----------------

第二层:
n = 2
处理 9
处理 20
q = [15,7]

ans = [[3], [9,20]]

----------------

第三层:
n = 2
处理 15
处理 7
q = []

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

这段代码最核心其实就是这四个变量:

复制代码
queue<TreeNode*> q;

表示:

等待处理的节点。

复制代码
int n = q.size();

表示:

当前这一层有几个节点。

复制代码
vector<int> layer;

表示:

当前这一层的答案。

复制代码
vector<vector<int>> ans;

表示:

所有层的最终答案。

你尤其要理解:

复制代码
int n = q.size();

为什么必须提前保存。

因为处理当前层的时候,会不断执行:

复制代码
q.push(node->left);
q.push(node->right);

下一层的节点加入队列。

例如第二层刚开始:

复制代码
q = [9,20]
n = 2

处理 20 后:

复制代码
q = [15,7]

但是我们不能继续处理 15、7,因为它们已经是第三层了。

所以使用:

复制代码
n

把当前层节点数量固定下来。

一句话记忆这道题:

外层 while 控制一层一层地走,内层 while 控制把当前这一层的所有节点处理完;处理当前节点时,把它的左右孩子加入队列,留给下一层处理。

相关推荐
果壳science14 分钟前
张祥前统一场论研讨会在香港理工大学举办
人工智能·算法
桐盛科技31 分钟前
能碳数据清洗用MAD算法,窗口大小和阈值怎么调?
物联网·算法·边缘计算
Omics Pro34 分钟前
澳科大:自进化AI虚拟细胞
数据库·人工智能·算法·机器学习·自然语言处理
Doubbbbbbble云35 分钟前
从算法工程化角度看空间换时间策略的权衡4
算法
心中有你02141 小时前
Java Swing实现校园最短路径导航系统(Dijkstra算法完整源码+详细解析)
java·开发语言·算法
Alphapeople1 小时前
路径规划算法的python实现
开发语言·python·算法
LabVIEW开发1 小时前
从饱和升温曲线到参数辨识:在LabVIEW中拟合y=a(1-e^(-bx))
算法·labview·labview知识·labview功能·labview程序
keyipatience1 小时前
5种IO模型与阻塞IO,select,poll,epoll,LT和ET模式
linux·服务器·网络·数据结构·c++·算法
路径规划6662 小时前
(MATLAB代码) 船舶轨迹聚类:K-means 算法(260912)
算法·matlab·kmeans
wzdark8 小时前
基于堆的优先队列实现原理与复杂度分析4
算法