玩樹的題目,十有八九都是遞歸,而遞歸的核心就是不停的DFS到葉結點,然后在回溯回去。在遞歸函數中,當我們遇到葉結點的時候,即沒有左右子結點,那么此時一條完整的路徑已經形成了,我們加上當前的葉結點后存入結果res中,然后回溯。
1、112. 路徑總和
給定一個二叉樹和一個目標和,判斷該樹中是否存在根節點到葉子節點的路徑,這條路徑上所有節點值相加等於目標和。
說明: 葉子節點是指沒有子節點的節點。
示例:
給定如下二叉樹,以及目標和 sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ \
7 2 1
返回 true, 因為存在目標和為 22 的根節點到葉子節點的路徑 5->4->11->2。
這道求二叉樹的路徑需要用深度優先算法DFS的思想來遍歷每一條完整的路徑,也就是利用遞歸不停找子節點的左右子節點,而調用遞歸函數的參數只有當前節點和sum值。首先,如果輸入的是一個空節點,則直接返回false,如果如果輸入的只有一個根節點,則比較當前根節點的值和參數sum值是否相同,若相同,返回true,否則false。 這個條件也是遞歸的終止條件。下面我們就要開始遞歸了,由於函數的返回值是Ture/False,我們可以同時兩個方向一起遞歸,中間用或||連接,只要有一個是True,整個結果就是True。遞歸左右節點時,這時候的sum值應該是原sum值減去當前節點的值。
/** * 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: bool hasPathSum(TreeNode* root, int sum) { if(root == nullptr || sum<=0) return false; if(root->left==nullptr && root->right==nullptr && root->val == sum) return true; return hasPathSum(root->left,sum-root->val) || hasPathSum(root->right, sum-root->val); } };
2、113. 路徑總和 II
給定一個二叉樹和一個目標和,找到所有從根節點到葉子節點路徑總和等於給定目標和的路徑。
說明: 葉子節點是指沒有子節點的節點。
示例:
給定如下二叉樹,以及目標和 sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ / \
7 2 5 1
返回:
[ [5,4,11,2], [5,8,4,5] ]
還是需要用深度優先搜索DFS,只不過數據結構相對復雜一點,需要用到二維的vector,而且每當DFS搜索到新節點時,都要保存該節點。而且每當找出一條路徑之后,都將這個保存為一維vector的路徑保存到最終結果二位vector中。並且,每當DFS搜索到子節點,發現不是路徑和時,返回上一個結點時,需要把該節點從一維vector中移除。
/** * 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: vector<vector<int>> pathSum(TreeNode* root, int sum) { vector<vector<int>> res; if(root == nullptr) return res; vector<int> temp; findPath(root,sum,temp,res); return res; } void findPath(TreeNode* root,int sum,vector<int> &temp,vector<vector<int>> &res) { if(root == nullptr) return; temp.push_back(root->val); sum -= root->val; if(root->left==nullptr && root->right==nullptr && sum==0) { res.push_back(temp); } else { findPath(root->left,sum,temp,res); findPath(root->right,sum,temp,res); } temp.pop_back(); } };
3、 437. 路徑總和 III
給定一個二叉樹,它的每個結點都存放着一個整數值。
找出路徑和等於給定數值的路徑總數。
路徑不需要從根節點開始,也不需要在葉子節點結束,但是路徑方向必須是向下的(只能從父節點到子節點)。
二叉樹不超過1000個節點,且節點數值范圍是 [-1000000,1000000] 的整數。
示例:
root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8
10
/ \
5 -3
/ \ \
3 2 11
/ \ \
3 -2 1
返回 3。和等於 8 的路徑有:
1. 5 -> 3
2. 5 -> 2 -> 1
3. -3 -> 11
這道題讓我們求二叉樹的路徑的和等於一個給定值,說明了這條路徑不必要從根節點開始,可以是中間的任意一段,而且二叉樹的節點值也是有正有負。那么我們可以用遞歸來做,相當於先序遍歷二叉樹,對於每一個節點都有記錄了一條從根節點到當前節點到路徑,同時用一個變量curSum記錄路徑節點總和,然后我們看curSum和sum是否相等,相等的話結果res加1,不等的話我們來繼續查看子路徑和有沒有滿足題意的,做法就是每次去掉一個節點,看路徑和是否等於給定值,注意最后必須留一個節點,不能全去掉了,因為如果全去掉了,路徑之和為0,而如果假如給定值剛好為0的話就會有問題。
/** * 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: int pathSum(TreeNode* root, int sum) { int res=0; if(root==nullptr) return res; vector<TreeNode*> temp; pathAll(root,sum,0,temp,res); return res; } void pathAll(TreeNode* root, int sum, int cursum, vector<TreeNode*> &temp, int &res) { if(root==nullptr) return; temp.push_back(root); cursum += root->val; if(cursum==sum) res++; int t = cursum; for(int i=0;i<temp.size()-1;++i) //要保留一個值,如果sum=0 { t -= temp[i]->val; if(t==sum) res++; } pathAll(root->left, sum, cursum,temp,res); pathAll(root->right, sum, cursum, temp, res); temp.pop_back(); } };
4、257. 二叉樹的所有路徑
給定一個二叉樹,返回所有從根節點到葉子節點的路徑。
說明: 葉子節點是指沒有子節點的節點。
示例:
輸入: 1 / \ 2 3 \ 5 輸出: ["1->2->5", "1->3"] 解釋: 所有根節點到葉子節點的路徑為: 1->2->5, 1->3
這道題給我們一個二叉樹,讓我們返回所有根到葉節點的路徑,跟之前那道Path Sum II很類似,比那道稍微簡單一些,不需要計算路徑和,只需要無腦返回所有的路徑即可,那么思路還是用遞歸來解,博主之前就強調過,玩樹的題目,十有八九都是遞歸,而遞歸的核心就是不停的DFS到葉結點,然后在回溯回去。在遞歸函數中,當我們遇到葉結點的時候,即沒有左右子結點,那么此時一條完整的路徑已經形成了,我們加上當前的葉結點后存入結果res中,然后回溯。注意這里結果res需要reference,而out是不需要引用的,不然回溯回去還要刪除新添加的結點,很麻煩。為了減少判斷空結點的步驟,我們在調用遞歸函數之前都檢驗一下非空即可,代碼而很簡潔,參見如下:
/** * 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: vector<string> binaryTreePaths(TreeNode* root) { vector<string> res; if(root==nullptr) return res; string temp; binary(root, temp, res); return res; } void binary(TreeNode* root, string temp, vector<string> &res ) { if(!root->left && !root->right) res.push_back(temp+to_string(root->val)); if(root->left) { binary(root->left, temp+to_string(root->val)+"->",res); } if(root->right) { binary(root->right, temp+to_string(root->val)+"->",res); } } };
