最近在刷树的题目,感觉很多都巧妙地利用了回溯法的思想,现总结如下:
回溯法某种意义上和DFS很像
二叉树的所有路径
准备找路径的时候,有一个直观上的感受就是,路径在分叉以前是合用一条的。
那么我们就不禁要思考,怎么做到前段路径的共用呢?
可以考虑维护一个后进先出的序列(实现手段可以是stack或者vector)
递归的时候先把遍历到的结点放进去;当遍历到叶子结点的时候,便是一条完整的路径。
回溯的时候把分岔口以后的结点丢出去,序列中维护的便是公共部分
我们前面提到了维护一个后进先出的序列,所以分岔口(中间结点)要先于岔路(左右子节点)进入该序列。
现在对选择遍历顺序的问题大家可能没有感觉,后面看到最近公共祖先那题的时候可以对比感受
/**
* 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:
vector<TreeNode*> vint;
vector<string> vstr;
void DFS(TreeNode* node){
string path;
vint.push_back(node);
if(node->left==nullptr && node->right==nullptr){
for(int i = 0;i < vint.size()-1;i++){
path += to_string(vint[i]->val);
path += "->";
}
path += to_string(vint[vint.size()-1]->val);
vstr.push_back(path);
return;
}
if(node->left!=nullptr){
DFS(node->left);
vint.pop_back();
}
if(node->right!=nullptr){
DFS(node->right);
vint.pop_back();
}
}
vector<string> binaryTreePaths(TreeNode* root) {
DFS(root);
return vstr;
}
};
路径总和Ⅰ
sum维护从根结点到当前结点的路径上经过的所有节点的和。
所以在经过当前结点时,把当前结点的val值加上;
如果回溯时,在路径上舍弃了该结点,则把舍弃结点的val值减去。
/**
* 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:
int sum = 0;
bool flag = 0;
void DFS(TreeNode* node,int target){
if(node==nullptr) return;
sum += node->val;
if(node->left==nullptr && node->right==nullptr){
if(sum==target) flag = 1;
return;
}
if(node->left!=nullptr){
DFS(node->left,target);
sum -= node->left->val;
}
if(node->right!=nullptr){
DFS(node->right,target);
sum -= node->right->val;
}
}
bool hasPathSum(TreeNode* root, int targetSum) {
DFS(root,targetSum);
return flag;
}
};
路径总和Ⅱ
path存放当前的单条路径,ans存放所有满足条件的路径
/**
* 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:
int sum = 0;
vector<int> path;
vector<vector<int>> ans;
void DFS(TreeNode* node,int target){
if(node==nullptr) return;
path.push_back(node->val);
sum += node->val;
if(node->left==nullptr && node->right==nullptr){
if(sum==target){
ans.push_back(path);
}
return;
}
if(node->left!=nullptr){
DFS(node->left,target);
sum -= node->left->val;
path.pop_back();
}
if(node->right!=nullptr){
DFS(node->right,target);
sum -= node->right->val;
path.pop_back();
}
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
DFS(root,targetSum);
return ans;
}
};
二叉树的最近公共祖先
236. 二叉树的最近公共祖先 - 力扣(LeetCode)
左右结点的最近公共祖先要先于中间结点进行更新,
这也决定了访问的次序是由叶子结点向上进行的。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root == nullptr || root == p || root == q) return root;
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
if (left == nullptr) return right;
if (right == nullptr) return left;
return root;
}
};