输入一棵二叉树的根节点和一个整数,按字典序打印出二叉树中节点值的和为输入整数的所有路径。路径定义为从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。
前置知识:
如果节点为root
,那么当前节点为叶子节点的必要条件是:
root.left == null && root.right == null
找出路径,当然需要遍历整棵树,遍历树的方式有很多种:前序、中序、后序,还有层序,这里选择前序遍历(根节点 --> 左子树 --> 右子树)。
具备了上述前置知识,这里无非增加了路径
和target
以及叶子节点
的判断。
涉及到树的遍历一般都是递归
,既然是递归,那么一个大问题就可以分为子问题来求解,且子问题的解决方式与大问题相同。只需改变一些动态参数即可!
父子问题分析:
这里以该问题为例,对父问题和子问题进行分析:
- 这里的父问题就是给我一个二叉树,让我去求满足
target
的所有路径,而这个父问题又可以分为两个子问题(如果左右子节点皆存在的话) - 对于这个树的左子节点,那么问题就是,以该左子节点为根节点的左子树,求满足
target-left.value
的所有路径。 - 以右子节点为根节点的右子树同理。
可以发现小问题可以和父问题共用一个函数进行求解,只不过参数的值是动态变化的而已。
递归算法三部曲:
- 明白递归函数的功能。例如这里的
FindPath(TreeNode root, int target)
,就是从根节点出发,找和为target
的所有路径 - 递归终止条件。当
root
节点为叶子节点,并且target == root.val
,表示找到了一条符合条件的路径 - 下一次递归。如果左子树不为空,则递归左子树
FindPath(root.left, target-left.value)
,右子树同理
用两个全局变量保存路径。
Java实现如下:
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
private ArrayList<ArrayList<Integer>> result = new ArrayList<>();
private ArrayList<Integer> list = new ArrayList<>();
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root, int target) {
if(root == null) return result;
list.add(root.val);
target -= root.val; // 更新target
if(target == 0 && root.left == null && root.right == null){ // target为0,且为叶子节点
result.add(new ArrayList<Integer>(list));
}
FindPath(root.left, target);
FindPath(root.right, target);
list.remove(list.size()-1);
return result;
}
}
如下图,为一个三层的二叉树,下面以该树为例,展示执行过程如下:
由于从始至终用的都是同一个list
,最后的list.remove(list.size()-1);
语句的目的是每当判断一个节点,都会将其删除,这样,当结束时,list
就是空的。
既然是同一个liist
,那么每当找到一个路径,不能将同一个list
添加到result
中,所以
result.add(new ArrayList<Integer>(list));
- 目的是新建一个
ArrayList
,将list
中元素复制到ArrayList
中,再将ArrayList
添加到result