1、题目描述:链接
输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。
示例:给定如下二叉树,以及目标和 sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ / \
7 2 5 1
返回:
[
[5,4,11,2],
[5,8,4,5]
]
2、思路:
二叉树路径搜索问题/矩阵路径搜索问题,首先想到DFS,但在路径不正确时需要退回,因此想到回溯法。
2.1、回溯法关键:
- 在递归函数开始前记录当前路径,递归函数结束后移除当前路径。
- 回溯法基本模式为for循环+递归函数,for循环可能展开变为各情况的遍历。
for循环,处理当前递归函数中参数可能的各种情况;
for循环内的递归函数,在当前情况下进行下一阶段递归。
2.2、回溯法伪代码:
思路链接,作者:luo-jing-yu-yu
result = []
void backtrack(路径,选择列表){
if 满足结束条件{
result.add(路径)
return ;
}
for 选择 in 选择列表{
做选择
backtrack(路径,选择列表)
撤销选择
}
}
核心是for循环中的递归,在递归调用之前“做选择”,在递归调用之后“撤销选择”。
做选择部分一般需要记录已经访问的元素避免重复访问,如添加used[]数组或visited[]数组。
例子:面试题38. 字符串的排列
3、C++代码:
class Solution {
private:
vector<vector<int>> ans;
public:
vector<vector<int>> pathSum(TreeNode* root, int sum) {
// 主函数入口
vector<int> tmp;
if(root == NULL) return ans;
findPath(tmp,root,sum);
return ans;
}
void findPath(vector<int>& tmp,TreeNode* root, int currVal){
//DFS
if(root == NULL) return ;
if(root->val == currVal && root->left == NULL && root->right == NULL){
// 终止条件
tmp.push_back(root->val);
ans.push_back(tmp);
tmp.pop_back();
return;
}
//递归核心部分代码:
tmp.push_back(root->val); // 记录当前路径
// 以下两行相当于for循环的展开,(实际为前序遍历)
findPath(tmp,root->left,currVal-root->val); //递归,当前递归函数结束返回时实现回溯
findPath(tmp,root->right,currVal-root->val);
// 遍历完了左右子树,需要将当前节点从子序列中移除掉,可以联想到回溯算法
tmp.pop_back(); // 移除当前路径,回溯的关键
}
};
类似题目:93. 复原IP地址
总结
1、回溯法需要记录路径时为在dfs函数末尾加pop()函数。
2、回溯法基本模式为for循环+递归函数,for循环可能展开变为各情况的遍历。