在做递归相关的题目时,经常需要在递归的过程中对返回结果进行更新或维护某个值作为判断条件,主要可以通过将该值作为成员变量、作为参数传递、在返回值中更新等三种方式,采用的方式不同,最终的递归程序就会不同。
下面以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