題目描述(中等難度)
從根節點到葉子節點的路徑組成一個數字,計算所有的數字和。
思路分析
和 112 題 有些像,112 題是給出一個 sum
,然後去找這條路徑。但本質上都一樣的,只需要對二叉樹進行遍歷,遍歷過程中記錄當前路徑的和就可以。說到遍歷,無非就是 BFS
和 DFS
,如果進行 BFS
,過程中我們需要維護多條路徑的和,所以我們選擇 DFS
。
說到 DFS
的話,可以用遞歸,也可以用棧去實現,遞歸會好理解一些,所以這裏就只介紹遞歸吧,棧的話前邊也用過很多了,可以看下 112 題 。
說到遞歸,既可以利用回溯的思想,也可以用分治的思想,這裏就用這兩種方式寫一下,關於回溯、分治,可以看一下 115 題,會有一個深刻的理解。
解法一 回溯法
回溯的思想就是一直進行深度遍歷,直到得到一個解後,記錄當前解。然後再回到之前的狀態繼續進行深度遍歷。
所以我們需要定義一個函數來得到這個解。
void dfs(TreeNode root, int cursum)
這個函數表示從根節點走到 root
節點的時候,路徑累積的和是 cursum
。
這裏我們用一個全局變量 sum
來保存每條路徑的和。
所以回溯的出口就是,當我們到達葉子節點,保存當前累計的路徑和。
private void dfs(TreeNode root, int cursum) {
if (root.left == null && root.right == null) {
sum += cursum;
return;
}
然後就是分別去嘗試左子樹和右子樹就可以。把所有的代碼合起來。
public int sumNumbers(TreeNode root) {
if (root == null) {
return 0;
}
dfs(root, root.val);
return sum;
}
int sum = 0;
private void dfs(TreeNode root, int cursum) {
//到達葉子節點
if (root.left == null && root.right == null) {
sum += cursum;
return;
}
//嘗試左子樹
if(root.left!=null){
dfs(root.left, cursum * 10 + root.left.val);
}
//嘗試右子樹
if(root.right!=null){
dfs(root.right, cursum * 10 + root.right.val);
}
}
解法二 分治法
分支法的思想就是,解決子問題,通過子問題解決最終問題。
要求一個樹所有的路徑和,我們只需要知道從根節點出發經過左子樹的所有路徑和和從根節點出發經過右子樹的所有路徑和,加起來就可以了。
所以我們需要定義一個函數。
int sumNumbersHelper(TreeNode root, int sum)
參數含義是經過當前 root
節點之前,已經累計的和是 sum
,函數返回從最初根節點經過當前 root
節點達到葉子節點的和。(明確函數的定義很重要,這樣纔可以保證正確的寫出遞歸)
所以如果經過當前節點,那麼當前已有路徑的和就是
int cursum = sum * 10 + root.val;
然後我們需要考慮經過當前 root
節點後,再經過它的左孩子到葉子節點的所有路徑和。
int ans1 = sumNumbersHelper(root.left,cursum)
再考慮經過當前 root
節點後,再經過它的右孩子到葉子節點的路徑和。
int ans2 = sumNumbersHelper(root.right,cursum)
兩個都算出來以後,加起來就是從最初根節點經過當前 root
節點到達葉子節點的所有路徑和了。
public int sumNumbers(TreeNode root) {
if (root == null) {
return 0;
}
return sumNumbersHelper(root, 0);
}
private int sumNumbersHelper(TreeNode root, int sum) {
//已經累計的和
int cursum = sum * 10 + root.val;
if (root.left == null && root.right == null) {
return cursum;
}
int ans = 0;
//從最開始經過當前 root 再經過左孩子到達葉子節點所有的路徑和
if (root.left != null) {
ans += sumNumbersHelper(root.left, cursum);
}
//從最開始經過當前 root 再經過右孩子到達葉子節點所有的路徑和
if (root.right != null) {
ans += sumNumbersHelper(root.right, cursum);
}
//返回從最開始經過當前 root 然後到達葉子節點所有的路徑和
return ans;
}
總
這道題本質上還是在考二叉樹的遍歷,回溯和分治的思想的區別也可以對比考慮一下。
更多詳細通俗題解詳見 leetcode.wang 。