LeetCode 257. 二叉树的所有路径
1、题目
题目链接:257. 二叉树的所有路径
给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。
叶子节点 是指没有子节点的节点。
示例 1:
输入:root = [1,2,3,null,5]
输出:["1->2->5","1->3"]
示例 2:
输入:root = [1]
输出:["1"]
提示:
- 树中节点的数目在范围 [1, 100] 内
- -100 <= Node.val <= 100
2、深度优先搜索(递归实现)
思路
这道题目要求返回从二叉树的根节点到所有叶子节点的路径。一种直观的解法是使用深度优先搜索(DFS)。
在深度优先搜索遍历二叉树时,我们需要考虑当前的节点以及它的孩子节点。
- 如果当前节点不是叶子节点,则在当前的路径末尾添加该节点,并继续递归遍历该节点的每一个孩子节点。
- 如果当前节点是叶子节点,则在当前路径末尾添加该节点后我们就得到了一条从根节点到叶子节点的路径,将该路径加入到答案即可。
如此,当遍历完整棵二叉树以后我们就得到了所有从根节点到叶子节点的路径。
代码
#include <iostream>
#include <vector>
#include <string>
using namespace std;
//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:
void traversal(TreeNode* cur, vector<int>& path, vector<string>& result) {
// 将当前节点的值添加到path中
path.push_back(cur->val);
// 判断当前节点是否是叶子节点(即没有左右子节点)
if (cur->left == nullptr && cur->right == nullptr) {
// 用于存储从根节点到当前叶子节点的路径。
string sPath;
// 将path里记录的路径转为string格式
for (int i = 0; i < path.size() - 1; i++) {
// 将path中的元素转换为字符串并添加到sPath中
sPath += to_string(path[i]);
// 添加箭头符号表示路径关系
sPath += "->";
}
// 将path中的最后一个元素(即当前叶子节点的值)转换为字符串,并添加到sPath中
sPath += to_string(path[path.size() - 1]);
// 将sPath添加到result中
result.push_back(sPath);
return;
}
// 如果当前节点有左子节点
if (cur->left) {
// 递归遍历左子树
traversal(cur->left, path, result);
// 回溯,将左子节点从path中移除
path.pop_back();
}
// 如果当前节点有右子节点
if (cur->right) {
// 递归遍历右子树
traversal(cur->right, path, result);
// 回溯,将右子节点从path中移除
path.pop_back();
}
}
vector<string> binaryTreePaths(TreeNode* root) {
// 用于存储所有从根节点到叶子节点的路径
vector<string> result;
// 用于存储从根节点到当前节点的路径
vector<int> path;
if (root == nullptr) {
return result;
}
traversal(root, path, result);
return result;
}
};
int main() {
Solution s;
TreeNode* root = new TreeNode(1);
root->left = new TreeNode(2);
root->right = new TreeNode(3);
root->left->left = new TreeNode(4);
root->left->right = new TreeNode(5);
root->right->left = new TreeNode(6);
root->right->right = new TreeNode(7);
vector<string> result = s.binaryTreePaths(root);
for (string path : result) {
cout << path << endl;
}
return 0;
}
复杂度分析
- 时间复杂度: O(n^2)
- 空间复杂度: O(n^2)
3、深度优先搜索(递归实现精简版)
思路
这里需要注意在函数定义的时候 void traversal(TreeNode* cur, string path, vector<string>& result)
,定义的是string path
,每次都是复制赋值,不用使用引用,否则就无法做到回溯的效果。
在** **traversal(cur->left, path + "->", result);
中的 path + "->"
隐藏了回溯操作。 每次函数调用完,path
依然是没有加上 "->"
的,这就是回溯了。
代码
class Solution {
private:
void traversal(TreeNode* cur, string path, vector<string>& result) {
// 将当前节点的值添加到path中
path += to_string(cur->val);
// 判断当前节点是否是叶子节点(即没有左右子节点)
if (cur->left == nullptr && cur->right == nullptr) {
// 将path添加到result中
result.push_back(path);
return;
}
// 如果当前节点有左子节点
if (cur->left) {
// 递归遍历左子树,路径加上 "->"
// 注意:这里的path加上 "->" 并不是在原path的基础上添加,而是直接在path末尾添加 "->"
// 这样每次函数调用完,path依然是没有加上"->" 的,这里隐藏的是回溯的操作
traversal(cur->left, path + "->", result);
}
// 如果当前节点有右子节点
if (cur->right) {
// 递归遍历右子树,路径加上 "->"
traversal(cur->right, path + "->", result);
}
}
public:
vector<string> binaryTreePaths(TreeNode* root) {
// 用于存储所有从根节点到叶子节点的路径
vector<string> result;
if (root == nullptr) {
return result;
}
// 用于存储从根节点到当前节点的路径
string path;
traversal(root, path, result);
return result;
}
};
复杂度分析
- 时间复杂度: O(n^2)
- 空间复杂度: O(n^2)
4、广度优先搜索(迭代法)
思路
我们也可以用广度优先搜索来实现。我们维护一个队列,存储节点以及根到该节点的路径。一开始这个队列里只有根节点。在每一步迭代中,我们取出队列中的首节点,如果它是叶子节点,则将它对应的路径加入到答案中。如果它不是叶子节点,则将它的所有孩子节点加入到队列的末尾。当队列为空时,广度优先搜索结束,我们即能得到答案。
代码
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
// 用于存储所有从根节点到叶子节点的路径
vector<string> paths;
if (root == nullptr) {
return paths;
}
// 使用队列存储树的遍历节点和对应的路径
queue<TreeNode*> queTree;
queue<string> quePath;
// 初始时将根节点和根节点的值加入队列
queTree.push(root);
quePath.push(to_string(root->val));
while (!queTree.empty()) {
// 取出队首节点和对应的路径
TreeNode* node = queTree.front();
queTree.pop();
string path = quePath.front();
quePath.pop();
// 如果当前节点是叶子节点,则将当前路径添加到paths中
if (node->left == nullptr && node->right == nullptr) {
paths.push_back(path);
}
// 如果当前节点有左子节点,则将左子节点和更新后的路径加入队列
if (node->left) {
queTree.push(node->left);
quePath.push(path + "->" + to_string(node->left->val));
}
// 如果当前节点有右子节点,则将右子节点和更新后的路径加入队列
if (node->right) {
queTree.push(node->right);
quePath.push(path + "->" + to_string(node->right->val));
}
}
return paths;
}
};
复杂度分析
- 时间复杂度: O(n^2)
- 空间复杂度: O(n^2)