代码随想录:二叉树篇(中)

226. 翻转二叉树

核心思路

翻转二叉树的本质是交换每个节点的左右子节点,关键在于选择合适的遍历顺序(前序、后序、层序均可,中序需特殊处理)。

一、递归法(前序遍历,最简洁)

cpp 复制代码
class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if (root == NULL) return root; // 终止条件:空节点直接返回
        swap(root->left, root->right); // 交换当前节点的左右子节点(中)
        invertTree(root->left);        // 递归翻转左子树(左)
        invertTree(root->right);       // 递归翻转右子树(右)
        return root;
    }
};
  • 逻辑:先处理当前节点(交换左右子节点),再递归处理左右子树,符合前序遍历"中-左-右"的顺序。

二、迭代法

1. 深度优先(前序,栈实现)
cpp 复制代码
class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if (root == NULL) return root;
        stack<TreeNode*> st;
        st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();
            st.pop();
            swap(node->left, node->right); // 处理当前节点
            // 栈是后进先出,先压右子树再压左子树,保证遍历顺序为左-右
            if (node->right) st.push(node->right);
            if (node->left) st.push(node->left);
        }
        return root;
    }
};
2. 广度优先(层序遍历,队列实现)
cpp 复制代码
class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if (root == NULL) return root;
        queue<TreeNode*> que;
        que.push(root);
        while (!que.empty()) {
            int size = que.size();
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                swap(node->left, node->right); // 处理当前层节点
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
        }
        return root;
    }
};

三、中序遍历说明

  • 递归中序遍历直接写会导致部分节点翻转两次,需特殊调整(不推荐);
  • 迭代版中序(统一写法)可正常使用,核心是通过栈标记节点,避免重复翻转。

总结

  1. 核心逻辑:交换每个节点的左右子节点,遍历顺序决定处理节点的时机;
  2. 推荐解法:递归前序(代码最简洁)、层序遍历(逻辑最直观);
  3. 避坑点:递归中序遍历需特殊处理,否则会重复翻转节点。

101. 对称二叉树(100.572同款)

核心思路

判断二叉树是否对称,本质是比较根节点的左子树和右子树是否互为镜像:需同时遍历两个子树,对比「左子树的外侧节点与右子树的内侧节点」「左子树的内侧节点与右子树的外侧节点」是否相等,核心采用类后序遍历逻辑(通过返回值判断结果)。

一、递归法

递归三部曲
  1. 参数与返回值:传入左右子树节点,返回布尔值表示是否对称;
  2. 终止条件:处理节点为空/数值不等的边界情况;
  3. 单层逻辑:递归对比外侧(左左&右右)、内侧(左右&右左)节点,最终返回二者的与结果。
cpp 复制代码
class Solution {
public:
    bool compare(TreeNode* left, TreeNode* right) {
        // 处理空节点情况
        if (left == NULL && right != NULL) return false;
        else if (left != NULL && right == NULL) return false;
        else if (left == NULL && right == NULL) return true;
        // 节点非空但数值不等
        else if (left->val != right->val) return false;

        // 递归对比外侧和内侧节点
        bool outside = compare(left->left, right->right);  // 左左 & 右右
        bool inside = compare(left->right, right->left);   // 左右 & 右左
        return outside && inside;
    }

    bool isSymmetric(TreeNode* root) {
        if (root == NULL) return true;
        return compare(root->left, root->right);
    }
};

二、迭代法

核心:用容器(队列/栈)成对存放需对比的节点,依次取出判断是否对称(非层序遍历)。

1. 队列实现
cpp 复制代码
class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        if (root == NULL) return true;
        queue<TreeNode*> que;
        que.push(root->left);
        que.push(root->right);

        while (!que.empty()) {
            TreeNode* leftNode = que.front(); que.pop();
            TreeNode* rightNode = que.front(); que.pop();

            // 均为空则继续对比下一组
            if (!leftNode && !rightNode) continue;
            // 一个为空/数值不等则不对称
            if (!leftNode || !rightNode || leftNode->val != rightNode->val) return false;

            // 成对加入需对比的节点
            que.push(leftNode->left);
            que.push(rightNode->right);
            que.push(leftNode->right);
            que.push(rightNode->left);
        }
        return true;
    }
};
2. 栈实现(仅替换容器类型)
cpp 复制代码
class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        if (root == NULL) return true;
        stack<TreeNode*> st;
        st.push(root->left);
        st.push(root->right);

        while (!st.empty()) {
            TreeNode* rightNode = st.top(); st.pop();
            TreeNode* leftNode = st.top(); st.pop();

            if (!leftNode && !rightNode) continue;
            if (!leftNode || !rightNode || leftNode->val != rightNode->val) return false;

            st.push(leftNode->left);
            st.push(rightNode->right);
            st.push(leftNode->right);
            st.push(rightNode->left);
        }
        return true;
    }
};

总结

  1. 核心逻辑:对比左右子树的「外侧+内侧」节点是否均相等;
  2. 递归关键:类后序遍历,通过返回值传递对比结果;
  3. 迭代关键:用队列/栈成对存放待对比节点,非传统层序遍历。

104. 二叉树的最大深度

核心思路

二叉树的最大深度 = 根节点到最远叶子节点的路径节点数。核心解法分两类:

  • 递归法:后序遍历求「高度」(根节点高度即最大深度),或前序遍历求「深度」(回溯记录最大值);
  • 迭代法:层序遍历统计层数(层数 = 最大深度)。

一、递归法(推荐后序遍历,简洁高效)

1. 后序遍历(求高度,最简洁)

核心逻辑:当前节点深度 = 1 + 左右子树深度的最大值(1 代表当前节点)。

cpp 复制代码
class Solution {
public:
    int maxDepth(TreeNode* root) {
        if (root == nullptr) return 0; // 空节点深度为0
        // 左子树深度 + 右子树深度,取最大值 +1(当前节点)
        return 1 + max(maxDepth(root->left), maxDepth(root->right));
    }
};
2. 前序遍历(求深度,体现回溯)

核心逻辑:遍历过程中记录当前深度,更新最大值(中左右顺序)。

cpp 复制代码
class Solution {
public:
    int result = 0; // 记录最大深度
    void getDepth(TreeNode* node, int depth) {
        result = max(result, depth); // 中:更新最大深度
        if (node->left == nullptr && node->right == nullptr) return;
        
        if (node->left) getDepth(node->left, depth + 1); // 左:深度+1
        if (node->right) getDepth(node->right, depth + 1); // 右:深度+1
    }

    int maxDepth(TreeNode* root) {
        if (root == nullptr) return 0;
        getDepth(root, 1); // 根节点深度从1开始
        return result;
    }
};

二、迭代法(层序遍历,直观)

核心逻辑:层序遍历的层数 = 二叉树最大深度。

cpp 复制代码
class Solution {
public:
    int maxDepth(TreeNode* root) {
        if (root == nullptr) return 0;
        int depth = 0;
        queue<TreeNode*> que;
        que.push(root);
        
        while (!que.empty()) {
            int size = que.size();
            depth++; // 每遍历一层,深度+1
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
        }
        return depth;
    }
};

总结

  1. 递归优先选后序遍历(代码极简,利用"根节点高度=最大深度"的特性);
  2. 迭代法选层序遍历(层数直接对应深度,逻辑直观);
  3. 前序遍历需借助回溯记录深度,适合理解"深度"的计算过程,但代码稍繁琐。

111. 二叉树的最小深度

核心思路

最小深度是「根节点到最近叶子节点」的路径节点数(叶子节点=左右孩子均为空)。核心坑点:不能直接取左右子树深度的最小值,需处理「单支树」(一侧子树为空)的情况。

一、递归法(后序遍历,推荐)

1. 完整逻辑版(易理解)
cpp 复制代码
class Solution {
public:
    int minDepth(TreeNode* root) {
        if (root == nullptr) return 0; // 空节点深度为0
        int leftDepth = minDepth(root->left);  // 左
        int rightDepth = minDepth(root->right); // 右

        // 关键:单支树需取非空侧深度+1,而非最小值
        if (root->left == nullptr && root->right != nullptr) {
            return 1 + rightDepth;
        }
        if (root->left != nullptr && root->right == nullptr) {
            return 1 + leftDepth;
        }
        // 左右子树均非空,取最小值+1
        return 1 + min(leftDepth, rightDepth);
    }
};
2. 前序遍历版(回溯记录最小值)
cpp 复制代码
class Solution {
private:
    int result = INT_MAX; // 初始化为极大值
    void getDepth(TreeNode* node, int depth) {
        if (node == nullptr) return;
        // 中:遇到叶子节点,更新最小深度
        if (node->left == nullptr && node->right == nullptr) {
            result = min(result, depth);
            return;
        }
        if (node->left) getDepth(node->left, depth + 1); // 左
        if (node->right) getDepth(node->right, depth + 1); // 右
    }

public:
    int minDepth(TreeNode* root) {
        if (root == nullptr) return 0;
        getDepth(root, 1); // 根节点深度从1开始
        return result;
    }
};

二、迭代法(层序遍历,高效)

核心逻辑:层序遍历中首次遇到叶子节点时的层数即为最小深度,可直接返回(无需遍历整棵树)。

cpp 复制代码
class Solution {
public:
    int minDepth(TreeNode* root) {
        if (root == nullptr) return 0;
        int depth = 0;
        queue<TreeNode*> que;
        que.push(root);

        while (!que.empty()) {
            int size = que.size();
            depth++; // 遍历一层,深度+1
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                // 首次遇到叶子节点,直接返回当前深度(最短路径)
                if (node->left == nullptr && node->right == nullptr) {
                    return depth;
                }
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
        }
        return depth;
    }
};

总结

  1. 核心坑点:单支树不能直接取min(left, right),需判断子树是否为空,取非空侧深度;
  2. 递归优先选后序遍历(逻辑简洁),迭代选层序遍历(首次遇到叶子节点即可返回,效率更高);
  3. 前序遍历需借助回溯记录最小深度,适合理解"深度"的计算过程。

222. 完全二叉树的节点个数

核心思路

本题有两种解法:

  1. 普通二叉树求解(递归/迭代遍历所有节点),时间复杂度 O(n);
  2. 利用完全二叉树特性求解(判断子树是否为满二叉树,用公式快速计算),时间复杂度 O(log²n)(更高效)。

一、普通二叉树解法(通用)

1. 递归法(后序遍历)

核心逻辑:节点总数 = 左子树节点数 + 右子树节点数 + 1(当前节点)。

cpp 复制代码
class Solution {
public:
    int countNodes(TreeNode* root) {
        if (root == nullptr) return 0; // 空节点数为0
        // 左 + 右 + 中(当前节点)
        return countNodes(root->left) + countNodes(root->right) + 1;
    }
};
2. 迭代法(层序遍历)

核心逻辑:遍历每一层节点,统计总数。

cpp 复制代码
class Solution {
public:
    int countNodes(TreeNode* root) {
        if (root == nullptr) return 0;
        queue<TreeNode*> que;
        que.push(root);
        int result = 0;

        while (!que.empty()) {
            int size = que.size();
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                result++; // 统计当前节点
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
        }
        return result;
    }
};

二、完全二叉树优化解法(推荐)

核心特性
  • 满二叉树:节点数 = 2^深度 - 1(深度从1开始);
  • 完全二叉树中,若左子树深度 = 右子树深度 → 左子树是满二叉树;否则右子树是满二叉树。
代码实现
cpp 复制代码
class Solution {
public:
    int countNodes(TreeNode* root) {
        if (root == nullptr) return 0;
        // 计算左子树深度(仅向左遍历)、右子树深度(仅向右遍历)
        TreeNode* left = root->left;
        TreeNode* right = root->right;
        int leftDepth = 0, rightDepth = 0;
        while (left) { left = left->left; leftDepth++; }
        while (right) { right = right->right; rightDepth++; }

        // 满二叉树:直接用公式计算
        if (leftDepth == rightDepth) {
            return (2 << leftDepth) - 1; // 2<<n 等价于 2^(n+1),适配depth初始为0
        }
        // 非满二叉树:递归统计左右子树
        return countNodes(root->left) + countNodes(root->right) + 1;
    }
};

总结

  1. 通用解法(递归/迭代):适配所有二叉树,逻辑简单但效率一般(O(n));
  2. 优化解法:利用完全二叉树+满二叉树特性,仅遍历树的高度次(O(log²n)),效率更高;
  3. 关键技巧:通过「左子树仅向左遍历、右子树仅向右遍历」判断是否为满二叉树,避免遍历所有节点。

110. 平衡二叉树

核心思路

平衡二叉树定义:每个节点的左右子树高度差绝对值 ≤ 1。核心解法为后序遍历求高度(从下往上判断,发现不平衡立即返回),迭代法效率低仅作补充。

一、递归法(推荐,高效)

核心逻辑:后序遍历求子树高度,若高度差>1则返回-1标记不平衡,否则返回当前节点高度。

cpp 复制代码
class Solution {
public:
    // 返回当前节点为根的树的高度,不平衡则返回-1
    int getHeight(TreeNode* node) {
        if (node == nullptr) return 0; // 空节点高度为0
        
        int leftH = getHeight(node->left);
        if (leftH == -1) return -1; // 左子树不平衡,直接返回
        
        int rightH = getHeight(node->right);
        if (rightH == -1) return -1; // 右子树不平衡,直接返回
        
        // 高度差>1则不平衡,否则返回当前节点高度
        return abs(leftH - rightH) > 1 ? -1 : 1 + max(leftH, rightH);
    }

    bool isBalanced(TreeNode* root) {
        return getHeight(root) != -1; // 非-1即平衡
    }
};

总结

  1. 递归法核心:后序遍历求高度,提前剪枝(子树不平衡直接返回-1),时间复杂度 O(n);
  2. 迭代法问题:重复计算子树高度,效率低(O(n²)),仅适合理解逻辑;
  3. 关键区分:求高度用后序遍历(从下到上),求深度用前序遍历(从上到下),本题需用高度判断平衡。

257. 二叉树的所有路径

核心思路

要求根节点到所有叶子节点的路径,需用前序遍历 (中左右)遍历节点,同时通过回溯回退路径,确保能遍历所有分支。核心有两种实现方式:递归(直观,易理解回溯)、迭代(模拟栈遍历)。

一、递归法(推荐,清晰体现回溯)

1. 显式回溯版(易理解)

vector<int> 记录路径,递归后手动回溯,适合新手理解回溯逻辑。

cpp 复制代码
class Solution {
private:
    void traversal(TreeNode* cur, vector<int>& path, vector<string>& res) {
        path.push_back(cur->val); // 中:记录当前节点
        // 终止条件:遇到叶子节点,拼接路径
        if (cur->left == nullptr && cur->right == nullptr) {
            string sPath;
            for (int i = 0; i < path.size() - 1; i++) {
                sPath += to_string(path[i]) + "->";
            }
            sPath += to_string(path.back());
            res.push_back(sPath);
            return;
        }
        // 左:递归+回溯
        if (cur->left) {
            traversal(cur->left, path, res);
            path.pop_back(); // 回溯,移除左子树节点
        }
        // 右:递归+回溯
        if (cur->right) {
            traversal(cur->right, path, res);
            path.pop_back(); // 回溯,移除右子树节点
        }
    }

public:
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> res;
        vector<int> path;
        if (root == nullptr) return res;
        traversal(root, path, res);
        return res;
    }
};
2. 隐式回溯版(精简)

string 传参(值传递),回溯隐藏在参数拷贝中,代码更简洁。

cpp 复制代码
class Solution {
private:
    void traversal(TreeNode* cur, string path, vector<string>& res) {
        path += to_string(cur->val); // 中:拼接当前节点
        if (cur->left == nullptr && cur->right == nullptr) {
            res.push_back(path);
            return;
        }
        // 左/右:path+"->" 是新字符串,原path不变(隐式回溯)
        if (cur->left) traversal(cur->left, path + "->", res);
        if (cur->right) traversal(cur->right, path + "->", res);
    }

public:
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> res;
        if (root == nullptr) return res;
        traversal(root, "", res);
        return res;
    }
};

总结

  1. 核心逻辑:前序遍历+回溯,前序确保路径从根到叶子,回溯确保遍历所有分支;
  2. 递归关键:
    • 显式回溯:用 vector 存路径,递归后必须 pop_back
    • 隐式回溯:用 string 值传递,参数拷贝自动实现回溯;
  3. 迭代法:双栈模拟递归,栈的"先进后出"特性需先处理右子树、后处理左子树。

404. 左叶子之和

核心思路

关键前提:左叶子的定义 ------某节点的左孩子不为空,且该左孩子的左右孩子均为空(即左孩子是叶子节点)。

核心:无法通过当前节点判断自身是否为左叶子,必须通过「父节点」判断其左孩子是否为左叶子。

解法分两类:递归(后序遍历累加,直观)、迭代(模拟遍历,灵活)。

一、递归法(推荐,易理解)

1. 完整逻辑版(清晰体现后序遍历)
cpp 复制代码
class Solution {
public:
    int sumOfLeftLeaves(TreeNode* root) {
        if (root == nullptr) return 0; // 空节点,左叶子和为0
        if (root->left == nullptr && root->right == nullptr) return 0; // 叶子节点,无左叶子

        int leftVal = sumOfLeftLeaves(root->left);  // 左:递归左子树
        // 关键:判断root的左孩子是否为左叶子
        if (root->left && !root->left->left && !root->left->right) {
            leftVal = root->left->val;
        }
        int rightVal = sumOfLeftLeaves(root->right); // 右:递归右子树

        return leftVal + rightVal; // 中:累加左右子树的左叶子和
    }
};
2. 精简版(代码简洁,逻辑不变)
cpp 复制代码
class Solution {
public:
    int sumOfLeftLeaves(TreeNode* root) {
        if (root == nullptr) return 0;
        int leftVal = 0;
        // 找到左叶子,记录其值
        if (root->left && !root->left->left && !root->left->right) {
            leftVal = root->left->val;
        }
        // 递归累加左、右子树的左叶子和
        return leftVal + sumOfLeftLeaves(root->left) + sumOfLeftLeaves(root->right);
    }
};

二、迭代法(前序遍历,模拟递归)

用栈遍历所有节点,遍历过程中判断每个节点的左孩子是否为左叶子,累加求和。

cpp 复制代码
class Solution {
public:
    int sumOfLeftLeaves(TreeNode* root) {
        if (root == nullptr) return 0;
        stack<TreeNode*> st;
        st.push(root);
        int result = 0;

        while (!st.empty()) {
            TreeNode* node = st.top();
            st.pop();
            // 判断当前节点的左孩子是否为左叶子,是则累加
            if (node->left && !node->left->left && !node->left->right) {
                result += node->left->val;
            }
            // 栈先进后出,先压右子树,再压左子树(保证前序遍历顺序)
            if (node->right) st.push(node->right);
            if (node->left) st.push(node->left);
        }
        return result;
    }
};

总结

  1. 核心坑点:区分「左节点」和「左叶子」,左叶子必须是「父节点的左孩子 + 自身为叶子」;
  2. 递归关键:后序遍历(左右中),通过父节点判断左叶子,累加所有符合条件的值;
  3. 迭代关键:栈模拟遍历,遍历每个节点时直接判断其左孩子是否为左叶子,高效直观;
  4. 复杂度:两种解法时间均为 O(n)(遍历所有节点),空间 O(log n)(递归栈/栈存储)。

513. 找树左下角的值

核心思路

目标:找到二叉树最后一行的最左侧节点值。核心解法分两类:

  • 递归法:前序遍历(左优先),记录「最大深度的叶子节点值」(最大深度对应最后一行,左优先确保最左侧);
  • 迭代法(层序遍历):更直观,记录最后一行第一个节点值即可。

一、递归法(左优先遍历,记录最大深度)

1. 显式回溯版(易理解)
cpp 复制代码
class Solution {
private:
    int maxDepth = INT_MIN; // 记录最大深度
    int result;             // 记录目标值

    void traversal(TreeNode* root, int depth) {
        // 终止条件:遇到叶子节点,更新最大深度和结果
        if (root->left == nullptr && root->right == nullptr) {
            if (depth > maxDepth) {
                maxDepth = depth;
                result = root->val;
            }
            return;
        }
        // 左:递归+回溯
        if (root->left) {
            depth++;
            traversal(root->left, depth);
            depth--; // 回溯
        }
        // 右:递归+回溯
        if (root->right) {
            depth++;
            traversal(root->right, depth);
            depth--; // 回溯
        }
    }

public:
    int findBottomLeftValue(TreeNode* root) {
        traversal(root, 0);
        return result;
    }
};
2. 隐式回溯版(精简)

回溯隐藏在 depth + 1 的参数传递中(值传递,原depth不变):

cpp 复制代码
class Solution {
private:
    int maxDepth = INT_MIN;
    int result;

    void traversal(TreeNode* root, int depth) {
        if (root->left == nullptr && root->right == nullptr) {
            if (depth > maxDepth) {
                maxDepth = depth;
                result = root->val;
            }
            return;
        }
        // 左优先,depth+1 隐式回溯
        if (root->left) traversal(root->left, depth + 1);
        if (root->right) traversal(root->right, depth + 1);
    }

public:
    int findBottomLeftValue(TreeNode* root) {
        traversal(root, 0);
        return result;
    }
};

二、迭代法(层序遍历,推荐)

核心:层序遍历逐行处理,记录每一行第一个节点值,最后一行的第一个值即为答案。

cpp 复制代码
class Solution {
public:
    int findBottomLeftValue(TreeNode* root) {
        queue<TreeNode*> que;
        if (root != nullptr) que.push(root);
        int result = 0;

        while (!que.empty()) {
            int size = que.size();
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                if (i == 0) result = node->val; // 记录当前行第一个节点
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
        }
        return result;
    }
};

总结

  1. 递归关键:左优先遍历 + 记录最大深度,最大深度的叶子节点就是最后一行最左侧节点;
  2. 迭代关键:层序遍历天然按行处理,只需记录最后一行第一个节点值,逻辑更直观;
  3. 回溯细节:精简版递归的 depth + 1 是隐式回溯,本质是参数值传递,原depth不受递归影响。

112. 113.路径总和

核心思路

目标:判断是否存在「根节点到叶子节点」的路径,节点值之和等于目标和。

核心解法:

  • 递归法:深度优先遍历(DFS),通过递减目标和(而非累加路径和)简化逻辑,找到符合条件的路径则立即返回;
  • 迭代法:栈模拟前序遍历,记录每个节点的路径和,遍历到叶子节点时判断是否匹配目标和。

一、递归法(推荐,易理解)

1. 显式回溯版(清晰体现回溯逻辑)
cpp 复制代码
class Solution {
private:
    bool traversal(TreeNode* cur, int count) {
        // 终止条件:叶子节点 + 剩余和为0 → 找到路径
        if (!cur->left && !cur->right && count == 0) return true;
        // 终止条件:叶子节点但剩余和不为0 → 无路径
        if (!cur->left && !cur->right) return false;

        // 左子树:递归+回溯
        if (cur->left) {
            count -= cur->left->val; // 递减剩余和
            if (traversal(cur->left, count)) return true;
            count += cur->left->val; // 回溯,恢复剩余和
        }
        // 右子树:递归+回溯
        if (cur->right) {
            count -= cur->right->val;
            if (traversal(cur->right, count)) return true;
            count += cur->right->val;
        }
        return false;
    }

public:
    bool hasPathSum(TreeNode* root, int sum) {
        if (root == nullptr) return false;
        // 初始剩余和 = 目标和 - 根节点值
        return traversal(root, sum - root->val);
    }
};
2. 精简版(隐式回溯,代码简洁)

回溯隐藏在参数传递中(sum - root->val 是值传递,原sum不变):

cpp 复制代码
class Solution {
public:
    bool hasPathSum(TreeNode* root, int sum) {
        if (root == nullptr) return false;
        // 叶子节点 + 剩余和等于当前节点值 → 找到路径
        if (!root->left && !root->right && sum == root->val) return true;
        // 递归左/右子树,只要有一个找到就返回true
        return hasPathSum(root->left, sum - root->val) || hasPathSum(root->right, sum - root->val);
    }
};

二、迭代法(栈模拟前序遍历)

核心:栈中存储「节点 + 到该节点的路径和」,遍历到叶子节点时判断路径和是否等于目标和。

cpp 复制代码
class Solution {
public:
    bool hasPathSum(TreeNode* root, int sum) {
        if (root == nullptr) return false;
        // 栈元素:pair<节点指针, 到该节点的路径和>
        stack<pair<TreeNode*, int>> st;
        st.push({root, root->val});

        while (!st.empty()) {
            auto [node, pathSum] = st.top();
            st.pop();

            // 叶子节点 + 路径和匹配 → 返回true
            if (!node->left && !node->right && pathSum == sum) return true;
            // 右子树入栈(栈先进后出,保证左优先)
            if (node->right) st.push({node->right, pathSum + node->right->val});
            // 左子树入栈
            if (node->left) st.push({node->left, pathSum + node->left->val});
        }
        return false;
    }
};

总结

  1. 递归关键:
    • 用「递减剩余和」替代「累加路径和」,简化终止条件判断;
    • 找到符合条件的路径后立即返回,无需遍历整棵树;
    • 精简版的回溯隐藏在参数值传递中,显式版更易理解回溯逻辑。
  2. 迭代关键:栈中记录「节点+路径和」,模拟递归遍历,叶子节点处校验结果;
  3. 核心区别:递归适合快速实现,迭代更直观体现遍历过程,两者时间/空间复杂度均为 O(n)(n为节点数)。
相关推荐
czxyvX1 小时前
011-C++之异常
c++
tod1131 小时前
C++核心知识点全解析(四)
开发语言·c++·面试经验
Zhu_S W1 小时前
深入理解哈希表:原理、源码与设计哲学
数据结构·散列表
期末考复习中,蓝桥杯都没时间学了1 小时前
力扣刷题23
算法·leetcode·职场和发展
闻缺陷则喜何志丹1 小时前
【计算几何 CAD】三点画弧、三点画圆是否是三角形的外接圆
c++·计算几何·cad··外接圆·
菜鸡儿齐1 小时前
leetcode-子集
算法·leetcode·深度优先
今儿敲了吗1 小时前
28| A-B数对
数据结构·c++·笔记·学习·算法
Desirediscipline2 小时前
#include<limits>#include <string>#include <sstream>#include <iomanip>
java·开发语言·前端·javascript·算法
Felven2 小时前
B. Luntik and Subsequences
算法