力扣日记:【二叉树篇】二叉树的所有路径
日期:2023.12.3
参考:代码随想录、力扣
257. 二叉树的所有路径
题目描述
难度:简单
给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。
叶子节点 是指没有子节点的节点。
示例 1:
输入:root = [1,2,3,null,5]
输出:[“1->2->5”,“1->3”]
示例 2:
输入:root = [1]
输出:[“1”]
提示:
- 树中节点的数目在范围 [1, 100] 内
- -100 <= Node.val <= 100
题解
/**
* 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 {
#define SOLUTION 2
public:
#if SOLUTION == 1
// 本体关键在于 递归+回溯
// 如对于[1,2,3,null,5]这样的二叉树, 首先遍历了1-2-5后, 需要回溯到1-2, 再回溯到1, 再继续遍历1-3
// 因此需要在每次递归后进行回溯(见代码)
// 至于遍历, 由于是从根节点遍历到叶子节点, 因此使用前序遍历(中-左-右)
vector<string> binaryTreePaths(TreeNode* root) {
vector<int> path;
vector<string> result;
if (root == NULL) return result;
traversal(root, path, result);
return result;
}
// 递归参数与返回值:参数为当前节点,当前路径以及结果集;返回值为空
void traversal(TreeNode* node, vector<int>& path, vector<string>& result) { // 注意是引用传递
// 递归处理(这里中节点的处理要放在终止条件前,因为终止条件需返回包含叶子节点的路径)
path.push_back(node->val);
// 递归终止条件:当遇到叶子节点时,将path放入结果集并返回
if (node->left == nullptr && node->right == nullptr) {
// 将path里记录的路径转为string格式
string sPath;
for (int i = 0; i < path.size() - 1; i++) {
sPath += to_string(path[i]);
sPath += "->";
}
sPath += to_string(path[path.size() - 1]); // 记录最后一个节点(叶子节点)
result.push_back(sPath); // 收集一个路径
return;
}
// 左节点
if (node->left) { // 防止叶子节点判断时为空
traversal(node->left, path, result);
path.pop_back(); // 回溯(遍历到叶子节点或左右节点都遍历了, 则要回溯到上一个节点)
}
if (node->right) {
traversal(node->right, path, result);
path.pop_back(); // 同理
}
}
#elif SOLUTION == 2 // 精简版
vector<string> binaryTreePaths(TreeNode* root) {
string path;
vector<string> result;
if (root == NULL) return result;
traversal(root, path, result);
return result;
}
// 这里直接用string,避免了vector<int>到string的转换
void traversal(TreeNode* node, string path, vector<string> &result) {
// 中
path += to_string(node->val);
// 终止条件
if (node->left == nullptr && node->right == nullptr) {
result.push_back(path);
return;
}
// 递归处理
if (node->left) {
// 注意这里的参数为 path + "->", 而不是先将path = path + "->"再传入path
// 同时string path也不是引用传递
// 也就是当递归结束后, path 仍为原来的path, 而不是 path + "->",不用显式回溯"->"
// 更不是完整路径(如果是引用传递则是如上面的完整路径),因此也不用显式回溯元素值
traversal(node->left, path + "->", result);
}
if (node->right) {
traversal(node->right, path + "->", result);
}
}
#endif
};
复杂度
时间复杂度:
空间复杂度:
思路总结
- 本题关键在于 递归+回溯
- 如对于[1,2,3,null,5]这样的二叉树, 首先遍历了1-2-5后, 需要回溯到1-2, 再回溯到1, 再继续遍历1-3
- 因此需要在每次递归后进行回溯(见代码)
- 至于遍历, 由于是从根节点遍历到叶子节点, 因此使用前序遍历(中-左-右)
- 前序遍历以及回溯的过程示意图
- 在精简版代码中,之所以不用主动显式回溯
- 是因为在递归函数的参数传递时,参数为 path + “->”, 而不是先将path = path + "->"再传入path
- 同时string path也不是引用传递
- 也就是当递归结束后, path 仍为原来的path, 而不是 path + “->”,不用显式回溯"->"
- 更不是完整路径(如果是引用传递则是如上面的完整路径),因此也不用显式回溯元素值
- 注意本次递归的中节点处理要放在终止条件之前,因为在终止条件时要存入包含此中节点的完整路径
- 关于本题的迭代法,目前还未学习…二刷再补充