代码随想录算法训练营第13天|二叉树的递归遍历, 迭代遍历,统一迭代, 层序遍历

二叉树遍历全面总结(递归+迭代+层序+经典习题)

一、递归遍历核心三要素

  1. 确定递归函数的参数和返回值:确定递归过程所需参数,明确返回值类型
  2. 确定终止条件:防止递归栈溢出,划定递归结束边界
  3. 确定单层递归逻辑:明确每层递归执行的业务逻辑,完成自身调用

前中后序递归写法逻辑简单,熟练掌握三要素即可快速写出。

二、迭代遍历(非递归实现)

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叉树childrenvector数组,不能使用->访问子节点

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;
    }
};

六、总结

  1. 递归遍历代码简洁,依靠三要素即可快速编写,适合刷题快速解题
  2. 迭代遍历依靠栈/队列实现,理解底层遍历原理,面试高频考察
  3. 层序遍历是二叉树必考题型,衍生出平均值、右视图、层序反转等大量变式题
  4. 做题核心技巧:区分二叉树与N叉树结构差异、区分整数运算与浮点运算、合理使用引用传参
posted @ 2026-05-20 22:50  wh67  阅读(5)  评论(0)    收藏  举报