代码随想录算法训练营 Day11 | 二叉树 part01

144. 二叉树的前序遍历

给你二叉树的根节点 root ,返回它节点值的 前序遍历。

cpp 复制代码
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        // 1. 边界条件处理:如果根节点为空,直接返回空数组
        if(root == NULL) return {};
        // 2. 初始化数据结构
        stack<TreeNode*> st; // 栈用于模拟递归调用栈
        vector<int> vec;     // 存储遍历结果
        // 3. 根节点入栈,作为遍历的起点
        st.push(root);
        // 4. 开始循环,直到栈为空(说明遍历结束)
        while(!st.empty()){
            // 4.1 弹出栈顶元素
            // 栈顶元素即为当前应该访问的"根"节点
            TreeNode* node = st.top();
            st.pop();
            // 4.2 访问节点:将节点值加入结果数组
            // 前序遍历顺序:根 -> 左 -> 右
            vec.push_back(node->val);
            // 4.3 右子节点入栈(如果存在)
            // 关键点:栈是后进先出(LIFO)结构
            // 因为要先处理左子树,所以必须让右子节点先入栈,沉到底部
            if(node->right) st.push(node->right);
            // 4.4 左子节点入栈(如果存在)
            // 左子节点后入栈,会位于栈顶,下次循环会被优先处理
            if(node->left) st.push(node->left);
        }
        return vec;
    }
};

总结

  1. 核心技巧:入栈顺序反着来
  • 前序遍历要求:根 →→ 左 →→ 右。
  • 栈是后进先出,想让左节点先出来,就得后入栈。
  • 所以代码逻辑:先压右,再压左。
  1. 复杂度
  • 时间 O(N) 每个节点进出栈各一次。
  • 空间 O(H) H 是树的高度。

145. 二叉树的后序遍历

给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历 。

cpp 复制代码
class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        // 1. 边界条件:空树直接返回
        if(root == NULL) return {};
        stack<TreeNode*> st;
        vector<int> vec;
        st.push(root);
        while(!st.empty()){
            TreeNode* node = st.top();
            st.pop();
            // 2. 记录节点值
            // 此时逻辑变成了:根 -> 右 -> 左
            vec.push_back(node->val);
            // 3. 入栈顺序调整
            // 因为栈是后进先出,想让右节点先处理,就得先压左节点
            // 这与前序遍历的入栈顺序正好相反
            if(node->left) st.push(node->left);
            if(node->right) st.push(node->right);
        }
        // 4. 关键一步:反转结果
        // 将"根 -> 右 -> 左"翻转为"左 -> 右 -> 根",即后序遍历
        reverse(vec.begin(), vec.end());
        return vec;
    }
};

总结

  1. 核心技巧:倒过来的前序遍历
  • 后序要求:左 →→ 右 →→ 根。
  • 逆向思维:先做"根 →→ 右 →→ 左"的遍历,最后把结果数组整体反转。
  • 操作变化:为了得到"根 →→ 右 →→ 左",入栈时需先左后右(保证右节点先出栈)。

2. 复杂度

  • 时间 O(N) 遍历加反转,总耗时仍是线性。
  • 空间 O(H) 取决于树的高度。

94. 二叉树的中序遍历

给定一个二叉树的根节点 root ,返回 它的 中序 遍历

cpp 复制代码
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        if(root==NULL) return {};
        stack<TreeNode*> st;
        vector<int> vec;
        TreeNode* cur = root; // 1. 初始化当前指针指向根节点
        // 循环条件:当前节点不为空 或 栈不为空
        while(cur || !st.empty()){
            if(cur){
                // 阶段一:一路向左走到底,将路径上的节点入栈
                st.push(cur);
                cur = cur->left;
            }
            else{
                // 阶段二:左边走到头了,处理栈顶节点
                cur = st.top(); st.pop(); // 弹出栈顶(最近访问的父节点)
                vec.push_back(cur->val);  // 【关键】加入结果集(左 -> 根)
                cur = cur->right;         // 转向右子树,开始新一轮的"左走到底"
            }
        }
        return vec;
    }
};

总结

  1. 核心总结

  2. 左路压栈:遇到节点先压栈,一直往左走,直到无路可走。

  3. 出栈处理:栈顶出栈,立即记录值,然后转向右子树。

  4. 模拟递归:栈保存了"回溯"的路径,保证了 左 -> 根 -> 右 的顺序。

  5. 复杂度

  • 时间复杂度:O(N)
    • 每个节点会被访问一次(压栈一次、出栈一次),无冗余操作。
  • 空间复杂度:O(N)
    • 栈的空间取决于树的高度。
    • 最坏情况(链表形状):树高为 N,栈需存储所有节点,空间 O(N)。
    • 最好情况(平衡二叉树):树高为 logN,空间 O(logN)。

102. 二叉树的层序遍历

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

cpp 复制代码
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        if(root==NULL) return {};
        queue<TreeNode*> que;
        vector<vector<int>> ans;
        que.push(root);
        while(!que.empty()){
            int size = que.size(); // 1. 关键点:获取当前层的节点数量
            vector<int> vec;
            // 2. 只循环当前层的数量,处理完一层
            for(int i = 0; i < size; i++){
                TreeNode* node = que.front(); que.pop(); // 队头出队
                vec.push_back(node->val); // 记录节点值
                // 3. 左右子节点依次入队(下一层的节点)
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
            }
            ans.push_back(vec); // 将当前层的结果加入总答案
        }
        return ans;
    }
};

总结

  1. 核心总结

  2. 队列特性:利用队列 先进先出 (FIFO) 特性,保证按从左到右的顺序处理节点。

  3. 层快照:循环前必须用 size 变量记录队列长度,这代表了当前层的节点数。

  4. 逐层剥离:内部 for 循环处理当前层并加入下一层节点,实现了层级分隔。

  5. 复杂度

  • 时间复杂度:O(N)
    • 每个节点只会进队一次、出队一次。
  • 空间复杂度:O(N)
    • 队列存储的节点数量最多为树的最大宽度(叶子节点总数),最坏情况下为 N。

相关题

相关推荐
美好的事情能不能发生在我身上2 小时前
Hot100中的:贪心专题
java·数据结构·算法
剑锋所指,所向披靡!3 小时前
数据结构之线性表
数据结构·算法
m0_672703315 小时前
上机练习第49天
数据结构·算法
样例过了就是过了5 小时前
LeetCode热题100 N 皇后
数据结构·c++·算法·leetcode·dfs·深度优先遍历
Z...........6 小时前
(优选算法)斐波那契数列模型
数据结构·算法
zyjyyds1136 小时前
和为0的四元组-双指针法(C语言实现)
c语言·数据结构·算法
像污秽一样7 小时前
算法设计与分析-习题6.1
数据结构·算法
样例过了就是过了7 小时前
LeetCode热题100 分割回文串
数据结构·c++·算法·leetcode·深度优先·dfs
Yvonne爱编码8 小时前
JAVA数据结构 DAY8-堆
java·数据结构·python