二叉樹中和爲某一值的路徑
原文地址:https://mintlucas.github.io/2019/04/20/二叉樹中路徑問題/
題目來源:劍指Offer
題目描述
輸入一顆二叉樹和一個整數,打印出二叉樹中結點值的和爲輸入整數的所有路徑。
路徑定義爲從樹的根結點開始往下一直到葉結點所經過的結點形成一條路徑。
時間限制:1秒 空間限制:32768K 熱度指數:167478
解題思路:
**帶記憶的 DFS**, 遞歸
提交記錄:
1. 遞歸結束條件始終有問題;
注意路徑終點是葉節點,而不是子節點
vector<vector<int>> res;
vector<int> buf;
void dfs(TreeNode *root, int expectNumber){
buf.push_back(root->val);
if(!root->left && !root->right && expectNumber == root->val)
res.push_back(buf);
//或前加進入判斷,空則不進僅走有路的情況,更像圖的深搜,終點路徑爲葉結點
if(root->left)
dfs(root->left, expectNumber - root->val);
if(root->right)
dfs(root->right, expectNumber - root->val);
buf.pop_back();
}
vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
if(root == NULL) return res;
dfs(root, expectNumber);
return res;
}
vector<vector<int>> res;
vector<int> buf;
vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
if(root == NULL) return;
expectNumber = expectNumber - root->val;
buf.push_back(root->val);
if (expectNumber == 0 && root->left == NULL && root->right == NULL) {
res.push_back(buf);
}
////不加if(root->right)但前要加爲空返回,多進一層
FindPath(root->left, expectNumber);
FindPath(root->right, expectNumber);
buf.pop_back();
return res;
}
LeetCode 129. Sum Root to Leaf Numbers
題目鏈接:(https://leetcode.com/problems/sum-root-to-leaf-numbers/)
解法1:先序遍歷的思想(根左右)+數字求和(每一層都比上層和*10+當前根節點的值)
PS:和求二叉樹深度等題及其類似,滿足葉子結點條件返回某值
int preOrder(TreeNode *root, int sum){
if(root == NULL)
return 0;
sum = sum*10 + root->val;
if(root->left == NULL && root->right == NULL)
return sum;
int sum1 = preOrder(root->left, sum);
int sum2 = preOrder(root->right, sum);
return sum1 + sum2;
}
int sumNumbers(TreeNode* root) {
int sum = preOrder(root, sum);
return sum;
}
更詳細版:從根結點開始,當每訪問到一個結點,我們把該結點添加到路徑上,並"累加"
該結點的值,這裏"累加"的含義指的是按照題目的要求組成相應的數字即"左移"後相加。
如果該結點爲葉結點,那麼一條路徑搜索完成,將當前所得結果累加。如果當前不是葉子
結點,則繼續訪問它 的子結點。當前結點訪問結束後,遞歸函數將自動回到它的父結點。
因此我們在函數退出之前要在路徑上"刪除"當前結點,做法就是將當前路徑值/10,以確
保返回父結點時路徑剛好是從根結點到父結點的路徑。我們不難看出保存路徑的數據結構
實際上是一個棧,因爲路徑要與遞歸調用狀態一致,而遞歸調用的本質就是一個壓棧和出
棧的過程。
class Solution {
public:
void sumNumbersCore(TreeNode *root, int &pathNum, int &sum) {
if(root){
pathNum = pathNum * 10 + root->val;
//如果是葉子節點,一條路徑搜索完成,累加到sum
if(!root->left && !root->right){
sum += pathNum;
}
//如果不是葉子節點,則遍歷它的子結點
else{
if(root->left)
sumNumbersCore(root->left, pathNum, sum);
if(root->right)
sumNumbersCore(root->right, pathNum, sum);
}
//返回父結點之前,路徑上“刪除”當前節點
pathNum /= 10;
}
}
int sumNumbers(TreeNode *root) {
int pathNum = 0;
int sum = 0;
sumNumbersCore(root, pathNum, sum);
return sum;
}
};
解法2:路徑問題一般用後續遍歷,遍歷到當前結點時棧裏即爲路徑結點,再求和即可
int sumOfVector(vector<int> sum){
int decimal = 1;
int res = 0;
for(int i = 0; i < sum.size(); ++i){
res *= 10;
res += sum[i];
}
return res;
}
int sumNumbers(TreeNode *root){
TreeNode* stk[1000] = {0};
int top = -1;
TreeNode *cur = root, *f_last = NULL;
vector<int> res;
long long totalSum = 0;
while(cur || top != -1){
if(cur){
stk[++top] = cur;
cur = cur->left;
}
else{
cur = stk[top];
if(cur->right && f_last != cur->right){
cur = cur->right;
}
else{
f_last = cur;
if(cur->left == NULL && cur->right == NULL){
vector<int> sum;
for(int i = 0; i <= top; ++i)
sum.push_back(stk[i]->val);
res.push_back(sumOfVector(sum));
}
top--;
cur = NULL;
}
}
}
for (int i = 0; i < res.size(); ++i)
totalSum += res[i];
return totalSum;
}