代码随想录算法训练营第13天|二叉树的递归遍历, 迭代遍历,统一迭代, 层序遍历
二叉树遍历全面总结(递归+迭代+层序+经典习题)
一、递归遍历核心三要素
- 确定递归函数的参数和返回值:确定递归过程所需参数,明确返回值类型
- 确定终止条件:防止递归栈溢出,划定递归结束边界
- 确定单层递归逻辑:明确每层递归执行的业务逻辑,完成自身调用
前中后序递归写法逻辑简单,熟练掌握三要素即可快速写出。
二、迭代遍历(非递归实现)
1. 前序遍历(中左右)
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> res;
if(root==NULL) return res;
st.push(root);
while(!st.empty()){
TreeNode* node=st.top();
st.pop();
res.push_back(node->val);
// 栈先进后出,先存右再存左,保证出栈顺序左->右
if(node->right) st.push(node->right);
if(node->left) st.push(node->left);
}
return res;
}
};
核心要点:入栈顺序为先右后左
2. 后序遍历(左右中)
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> res;
if(root==NULL) return res;
st.push(root);
while(!st.empty()){
TreeNode* node=st.top();
st.pop();
res.push_back(node->val);
// 调换左右入栈顺序
if(node->left) st.push(node->left);
if(node->right) st.push(node->right);
}
// 反转数组得到后序结果
reverse(res.begin(),res.end());
return res;
}
};
核心要点:复用前序遍历逻辑,调换入栈顺序后反转结果数组
3. 中序遍历(左中右)
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
if(root==NULL) return res;
stack<TreeNode*> st;
TreeNode*cur=root;
// 节点不为空 或 栈内有元素继续遍历
while(!cur==NULL||!st.empty()){
if(cur!=NULL){
st.push(cur);
cur=cur->left;
}
else{
cur=st.top();
st.pop();
res.push_back(cur->val);
cur=cur->right;
}
}
return res;
}
};
三、统一迭代遍历写法
1. 空指针标记法(推荐)
原理:首次遍历节点直接入栈,需要处理的节点后方添加空指针标记,弹出空指针时再存入结果集
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
if(root==NULL)return res;
stack<TreeNode*> st;
st.push(root);
while(!st.empty()){
TreeNode* node=st.top();
if(node!=NULL){
st.pop();
if(node->right) st.push(node->right);
st.push(node);
st.push(NULL);
if(node->left) st.push(node->left);
}
else{
st.pop();
node=st.top();
res.push_back(node->val);
st.pop();
}
}
return res;
}
};
2. 布尔标记法
原理:用布尔值区分节点遍历状态,false代表首次遍历,true代表二次遍历可存入结果
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
if(root==NULL) return res;
stack<pair<TreeNode*,bool>> st;
st.push(make_pair(root,false));
while(!st.empty()){
TreeNode* node=st.top().first;
bool visited=st.top().second;
st.pop();
if(visited){
res.push_back(node->val);
continue;
}
if(node->right) st.push(make_pair(node->right,false));
st.push(make_pair(node,true));
if(node->left) st.push(make_pair(node->left,false));
}
return res;
}
};
对比总结:空指针标记法代码更简洁,无需构造键值对,日常刷题优先使用
四、层序遍历(BFS广度优先)
1. 队列标准实现
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
if(root==NULL) return res;
queue<TreeNode*> que;
que.push(root);
while(!que.empty()){
int size=que.size();
vector<int> vec;
// 固定每层节点数量,避免队列长度动态变化出错
for(int i=0;i<size;i++){
TreeNode* node=que.front();
que.pop();
vec.push_back(node->val);
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
res.push_back(vec);
}
return res;
}
};
2. 递归实现层序遍历
# 递归法
class Solution {
public:
// 必须加引用&,操作原数组而非副本
void order(TreeNode*node,vector<vector<int>>&res,int depth){
if(node==NULL) return;
// 新层级创建空数组存储元素
if(depth==res.size()) res.push_back(vector<int>());
res[depth].push_back(node->val);
order(node->left,res,depth+1);
order(node->right,res,depth+1);
}
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
int depth=0;
order(root,res,depth);
return res;
}
};
重点注意:递归中修改外部容器必须加&引用,否则只会修改临时副本,结果为空
五、层序遍历经典刷题习题
1. LeetCode 107 二叉树的层序遍历 II
自底向上输出层序结果,直接反转正常层序结果即可
class Solution {
public:
vector<vector<int>> levelOrderBottom(TreeNode* root) {
vector<vector<int>> res;
if(root==NULL) return res;
queue<TreeNode*> que;
que.push(root);
while(!que.empty()){
int size=que.size();
vector<int> vec;
for(int i=0;i<size;i++){
TreeNode* node=que.front();
que.pop();
vec.push_back(node->val);
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
res.push_back(vec);
}
// 反转数组实现倒序层序
reverse(res.begin(),res.end());
return res;
}
};
2. LeetCode 199 二叉树的右视图
递归优先遍历右子树,每层首个元素即为右视图节点
class Solution {
public:
void order(TreeNode*node,vector<vector<int>>&res,int depth){
if(node==NULL) return;
if(depth==res.size()) res.push_back(vector<int>());
res[depth].push_back(node->val);
// 优先遍历右子树
order(node->right,res,depth+1);
order(node->left,res,depth+1);
}
vector<int> rightSideView(TreeNode* root) {
vector<vector<int>> res1;
int depth=0;
order(root,res1,depth);
vector<int>res2;
for(int i=0;i<res1.size();i++){
res2.push_back(res1[i][0]);
}
return res2;
}
};
3. LeetCode 637 二叉树的层平均值
遍历每层节点求和计算平均值,注意浮点类型避免整数除法
class Solution {
public:
vector<double> averageOfLevels(TreeNode* root) {
vector<vector<int>> res;
vector<double> res2;
if(root==NULL) return res2;
queue<TreeNode*> que;
que.push(root);
while(!que.empty()){
int size=que.size();
vector<int> vec;
for(int i=0;i<size;i++){
TreeNode* node=que.front();
que.pop();
vec.push_back(node->val);
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
res.push_back(vec);
}
for(int i=0;i<res.size();i++){
int num=0;
// 求和使用double类型,保留小数结果
double sum=0;
for(int j=0;j<res[i].size();j++){
sum+=res[i][j];
num++;
}
res2.push_back(sum/num);
}
return res2;
}
};
4. LeetCode 429 N 叉树的层序遍历
N叉树无左右孩子,通过children数组遍历所有子节点
/*
// Definition for a Node.
class Node {
public:
int val;
vector<Node*> children;
Node() {}
Node(int _val) {
val = _val;
}
Node(int _val, vector<Node*> _children) {
val = _val;
children = _children;
}
};
*/
class Solution {
public:
vector<vector<int>> levelOrder(Node* root) {
vector<vector<int>> res;
if(root==NULL) return res;
queue<Node*> que;
que.push(root);
while(!que.empty()){
int size=que.size();
vector<int>temp;
for(int i=0;i<size;i++){
Node*cur=que.front();
que.pop();
temp.push_back(cur->val);
// 遍历当前节点所有子节点
for(auto x:cur->children){
if(x!=NULL){
que.push(x);
}
}
}
res.push_back(temp);
}
return res;
}
};
知识点:N叉树children是vector数组,不能使用->访问子节点
5. LeetCode 515 在每个树行中找最大值
层序遍历过程中记录每层最大值即可
class Solution {
public:
vector<int> largestValues(TreeNode* root) {
vector<int>res;
if(root==NULL) return res;
queue<TreeNode*> que;
que.push(root);
while(!que.empty()){
int size=que.size();
int max=INT_MIN;
for(int i=0;i<size;i++){
TreeNode* node=que.front();
que.pop();
if(node->val>max) max=node->val;
if(node->left) que.push(node->left);
if(node->right) que.push(node->right);
}
res.push_back(max);
}
return res;
}
};
6. LeetCode 116 / 117 填充每个节点的下一个右侧节点指针
BFS 通用解法
class Solution {
public:
Node* connect(Node* root) {
queue<Node*> que;
if (root != NULL) que.push(root);
vector<vector<Node*>> result;
while (!que.empty()) {
int size = que.size();
vector<Node*> vec;
for (int i = 0; i < size; i++) {
Node* node = que.front();
que.pop();
vec.push_back(node);
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
result.push_back(vec);
}
// 同层节点依次连接next指针
for(int i=0;i<result.size();i++){
for(int j=0;j<result[i].size()-1;j++){
result[i][j]->next=result[i][j+1];
}
}
return root;
}
};
复杂度:时间O(N),空间O(N)
O(1) 常数空间最优解法
class Solution {
public:
Node* connect(Node* root) {
Node*cur=root;
while(cur){
Node*pre=NULL;
Node*head=NULL;
for(Node*p=cur;p;p=p->next){
if(p->left){
if(!head) head=p->left;
if(pre) pre->next=p->left;
pre=p->left;
}
if(p->right){
if(!head) head=p->right;
if(pre) pre->next=p->right;
pre=p->right;
}
}
cur=head;
}
return root;
}
};
复杂度:时间O(N),空间O(1)
7. LeetCode 104. 二叉树的最大深度
BFS 层序解法
class Solution {
public:
int maxDepth(TreeNode* root) {
queue<TreeNode*> que;
if (root != NULL) que.push(root);
vector<vector<int>> result;
while (!que.empty()) {
int size = que.size();
vector<int> vec;
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
vec.push_back(node->val);
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
result.push_back(vec);
}
// 层数即为最大深度
return result.size();
}
};
DFS 递归最简解法
class Solution {
public:
int maxDepth(TreeNode* root) {
if(root==NULL) return 0;
return max(maxDepth(root->left),maxDepth(root->right))+1;
}
};
LeetCode 111. 二叉树的最小深度
最小深度定义:根节点到最近叶子节点的最短路径节点数
class Solution {
public:
int minDepth(TreeNode *root) {
// 空树深度为0
if (root == nullptr) {
return 0;
}
// 叶子节点深度为1
if (root->left == nullptr && root->right == nullptr) {
return 1;
}
int min_depth = INT_MAX;
// 仅遍历存在的子树,排除单边为空的无效路径
if (root->left != nullptr) {
min_depth = min(minDepth(root->left), min_depth);
}
if (root->right != nullptr) {
min_depth = min(minDepth(root->right), min_depth);
}
return min_depth + 1;
}
};
六、总结
- 递归遍历代码简洁,依靠三要素即可快速编写,适合刷题快速解题
- 迭代遍历依靠栈/队列实现,理解底层遍历原理,面试高频考察
- 层序遍历是二叉树必考题型,衍生出平均值、右视图、层序反转等大量变式题
- 做题核心技巧:区分二叉树与N叉树结构差异、区分整数运算与浮点运算、合理使用引用传参

浙公网安备 33010602011771号