给你一个二叉树的根节点 root
,按 任意顺序 ,返回所有从根节点到叶子节点的路径。
叶子节点 是指没有子节点的节点。
class Solution {
public:
//能进入countPath中,说明该节点不为nullptr
void countPath(TreeNode* cur, vector<int>& path, vector<string>& result){
//把当前遍历到的二叉树节点的值放入到path中,因为当前遍历到的节点是当前路径中的一个节点
path.push_back(cur->val);
//递归结束条件
//如果遍历到孩子节点,说明找到了一条完整的路径
if(cur->left == nullptr && cur->right == nullptr){
//n表示当前path中的节点数有多少
int n = path.size();
string curPath = ""; //curPath表示当前找到的路径的字符串形式,先初始化为空串
//假设当前 path 中的值为1,2,5
//for循环将每一个数字后面紧跟着一个 "->" 符号,由于 5 后面没有 "->" ,所以for循环只遍历到 2 的位置。
for(int i = 0; i < n-1; ++i){
curPath += to_string(path[i]);
curPath += "->";
}
//由于上面的for循环没有将 5 放入到当前找到的路径的字符串形式中,所以要把 5 加进去
curPath += to_string(path[n-1]);
//这样curPath就是当前遍历到的路径的完整字符串形式将他放入到结果集中
result.push_back(curPath);
}
//如果当前节点存在左孩子,就向左进行遍历
if(cur->left){
countPath(cur->left, path, result);
//回溯和递归是一一对应的,有一个递归,就要有一个回溯
path.pop_back();//回溯,为了回到上一次进行递归的节点
}
//如果当前节点存在左孩子,就向左进行遍历
if(cur->right){
countPath(cur->right, path, result);
path.pop_back();
}
}
vector<string> binaryTreePaths(TreeNode* root) {
vector<int> path;//存放当前遍历的路径上的各节点值
vector<string> result;//存放每一条路径的字符串,形如"1->2->5"
//如果我们给定的二叉树的根节点为空,我们直接返回
if(root == nullptr){
return result;
}
//如果给定的二叉树不为空,进入函数
countPath(root, path, result);
//返回结果数组
return result;
}
// 时间复杂度:O(N^2),其中N表示节点数目。在深度优先搜索中每个节点会被访问一次且只会被访问一次,path数组进行遍历,时间代价为O(N),故时间复杂度为O(N2)。
// 空间复杂度:O(N^2),其中N表示节点数目。除答案数组外我们需要考虑递归调用的栈空间。在最坏情况下,当二叉树中每个节点只有一个孩子节点时,即整棵二叉树呈一个链状,此时递归的层数为N,此时每一层的path变量的空间代价的总和为O(N^2)空间复杂度为O(N2)。最好情况下,当二叉树为平衡二叉树时,它的高度为log N,此时空间复杂度为O((log N)^2)。
};
简洁形式,隐藏了回溯的过程。
class Solution {
public:
void countPath(TreeNode* cur, string path, vector<string>& result){
path += to_string(cur->val);
if(cur->left == nullptr && cur->right == nullptr){
result.push_back(path);
}
if(cur->left){
countPath(cur->left, path+"->", result);
}
if(cur->right){
countPath(cur->right,path+"->",result);
}
}
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> result;
string path;
if(root == nullptr){
return result;
}
countPath(root, path, result);
return result;
}
};
注意在函数定义的时候
void countPath(TreeNode* cur, string path, vector<string>& result)
,定义的是string path
,每次都是复制赋值,不用使用引用,否则就无法做到回溯的效果。那么在如上代码中,貌似没有看到回溯的逻辑,其实不然,回溯就隐藏在
countPath(cur->left, path + "->", result);
中的path + "->"
。 每次函数调用完,path依然是没有加上"->" 的,这就是回溯了。