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;
}
};
三、中序遍历说明
- 递归中序遍历直接写会导致部分节点翻转两次,需特殊调整(不推荐);
- 迭代版中序(统一写法)可正常使用,核心是通过栈标记节点,避免重复翻转。
总结
- 核心逻辑:交换每个节点的左右子节点,遍历顺序决定处理节点的时机;
- 推荐解法:递归前序(代码最简洁)、层序遍历(逻辑最直观);
- 避坑点:递归中序遍历需特殊处理,否则会重复翻转节点。
101. 对称二叉树(100.572同款)
核心思路
判断二叉树是否对称,本质是比较根节点的左子树和右子树是否互为镜像:需同时遍历两个子树,对比「左子树的外侧节点与右子树的内侧节点」「左子树的内侧节点与右子树的外侧节点」是否相等,核心采用类后序遍历逻辑(通过返回值判断结果)。
一、递归法
递归三部曲
- 参数与返回值:传入左右子树节点,返回布尔值表示是否对称;
- 终止条件:处理节点为空/数值不等的边界情况;
- 单层逻辑:递归对比外侧(左左&右右)、内侧(左右&右左)节点,最终返回二者的与结果。
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;
}
};
总结
- 核心逻辑:对比左右子树的「外侧+内侧」节点是否均相等;
- 递归关键:类后序遍历,通过返回值传递对比结果;
- 迭代关键:用队列/栈成对存放待对比节点,非传统层序遍历。
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;
}
};
总结
- 递归优先选后序遍历(代码极简,利用"根节点高度=最大深度"的特性);
- 迭代法选层序遍历(层数直接对应深度,逻辑直观);
- 前序遍历需借助回溯记录深度,适合理解"深度"的计算过程,但代码稍繁琐。
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;
}
};
总结
- 核心坑点:单支树不能直接取min(left, right),需判断子树是否为空,取非空侧深度;
- 递归优先选后序遍历(逻辑简洁),迭代选层序遍历(首次遇到叶子节点即可返回,效率更高);
- 前序遍历需借助回溯记录最小深度,适合理解"深度"的计算过程。
222. 完全二叉树的节点个数
核心思路
本题有两种解法:
- 按普通二叉树求解(递归/迭代遍历所有节点),时间复杂度 O(n);
- 利用完全二叉树特性求解(判断子树是否为满二叉树,用公式快速计算),时间复杂度 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;
}
};
总结
- 通用解法(递归/迭代):适配所有二叉树,逻辑简单但效率一般(O(n));
- 优化解法:利用完全二叉树+满二叉树特性,仅遍历树的高度次(O(log²n)),效率更高;
- 关键技巧:通过「左子树仅向左遍历、右子树仅向右遍历」判断是否为满二叉树,避免遍历所有节点。
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),时间复杂度 O(n);
- 迭代法问题:重复计算子树高度,效率低(O(n²)),仅适合理解逻辑;
- 关键区分:求高度用后序遍历(从下到上),求深度用前序遍历(从上到下),本题需用高度判断平衡。
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;
}
};
总结
- 核心逻辑:前序遍历+回溯,前序确保路径从根到叶子,回溯确保遍历所有分支;
- 递归关键:
- 显式回溯:用
vector存路径,递归后必须pop_back; - 隐式回溯:用
string值传递,参数拷贝自动实现回溯;
- 显式回溯:用
- 迭代法:双栈模拟递归,栈的"先进后出"特性需先处理右子树、后处理左子树。
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;
}
};
总结
- 核心坑点:区分「左节点」和「左叶子」,左叶子必须是「父节点的左孩子 + 自身为叶子」;
- 递归关键:后序遍历(左右中),通过父节点判断左叶子,累加所有符合条件的值;
- 迭代关键:栈模拟遍历,遍历每个节点时直接判断其左孩子是否为左叶子,高效直观;
- 复杂度:两种解法时间均为 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;
}
};
总结
- 递归关键:左优先遍历 + 记录最大深度,最大深度的叶子节点就是最后一行最左侧节点;
- 迭代关键:层序遍历天然按行处理,只需记录最后一行第一个节点值,逻辑更直观;
- 回溯细节:精简版递归的
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;
}
};
总结
- 递归关键:
- 用「递减剩余和」替代「累加路径和」,简化终止条件判断;
- 找到符合条件的路径后立即返回,无需遍历整棵树;
- 精简版的回溯隐藏在参数值传递中,显式版更易理解回溯逻辑。
- 迭代关键:栈中记录「节点+路径和」,模拟递归遍历,叶子节点处校验结果;
- 核心区别:递归适合快速实现,迭代更直观体现遍历过程,两者时间/空间复杂度均为 O(n)(n为节点数)。