在做遞歸相關的題目時,經常需要在遞歸的過程中對返回結果進行更新或維護某個值作爲判斷條件,主要可以通過將該值作爲成員變量、作爲參數傳遞、在返回值中更新等三種方式,採用的方式不同,最終的遞歸程序就會不同。
下面以LeetCode的404-Sum of Left Leaves爲例講解三種方式如何實現。
題目大意主要是說讓我們求一顆二叉樹的所有左葉子值之和。
Example:
3
/ \
9 20
/ \
15 7
There are two left leaves in the binary tree, with values 9 and 15 respectively. Return 24.
這道題就是二叉樹的遞歸遍歷,在遞歸的過程中需要更新維護兩個值,一個是最終的和sum
,一個是判斷該結點是否爲左葉子結點的標誌flag
1. 作爲成員變量
首先考慮我認爲最容易想到的做法,就是將sum
和flag
這兩個值作爲成員變量,相當於全局變量,每次遞歸更新。
Java代碼如下:
class Solution {
private int sum = 0;
private boolean flag = false;
public int sumOfLeftLeaves(TreeNode root) {
if(root == null) return 0;
if(root.left == null && root.right == null && flag == true) {
sum += root.val;
}
if(root.left != null) {
flag = true;
sumOfLeftLeaves(root.left);
}
if(root.right != null) {
flag = false;
sumOfLeftLeaves(root.right);
}
return sum;
}
}
flag
爲true時表示該結點爲左葉子結點,在向左子樹遍歷時更新該值爲true,往右子樹遍歷時更新該值爲false。
2. 在返回值中更新
其實在遞歸遍歷時也可以不需要顯示聲明上述sum
變量,而是將其在返回值中進行累積,而flag
變量也可以在代碼中進行判斷。
實現代碼如下:
class Solution {
public int sumOfLeftLeaves(TreeNode root) {
if(root == null) return 0;
//判斷結點的左孩子是否是葉子結點(即左葉子結點)
if(root.left != null && root.left.left == null && root.left.right == null) {
return root.left.val + sumOfLeftLeaves(root.right);
}
return sumOfLeftLeaves(root.left) + sumOfLeftLeaves(root.right);
}
}
上述並沒有顯式維護sum
的值,而是通過遞歸返回的結果中對結果進行累加。
3. 作爲參數傳遞
上述方法2中有個問題,就是在數據量大時遞歸會佔用大量堆棧空間,我們可以對其進行尾遞歸優化,即將要更新維護的值放入遞歸函數的參數中進行傳遞。
實現代碼如下:
class Solution {
public int sumOfLeftLeaves(TreeNode root) {
if(root == null) return 0;
int[] sum = new int[1];
sumOfLeftLeaves(root.left, sum, true);
sumOfLeftLeaves(root.right, sum, false);
return sum[0];
}
private void sumOfLeftLeaves(TreeNode node, int[] sum, boolean flag) {
if(node == null) return;
if(node.left == null && node.right == null && flag == true)
sum[0] += node.val;
sumOfLeftLeaves(node.left, sum, true);
sumOfLeftLeaves(node.right, sum, false);
}
}
由於Java中並沒有C++中的傳地址操作,所以這裏將sum作爲數組進行傳遞。
參考:
(1)關於尾遞歸
(2)https://www.cnblogs.com/grandyang/p/5923559.html