题目描述
输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)
给出一个二叉树:
接下来列表了解上面二叉树的遍历过程。
观察上表的遍历过程,思路总结如下:
1.当用前序遍历的方式访问到某一节点时,则把该节点添加到路径上,并累加该节点的值。
2.如果该节点为叶节点,且路径中节点的和刚好等于输入的整数,则当前路径符合要求,把当前路径打印出来。如果当前路径不是叶节点,则继续访问它的子节点。
3.当前节点访问结束,递归函数会自动回到它的父节点,需要注意的是,在函数退出之前要在路径上删除当前节点并减去当前节点的值,以确保返回父节点的路径刚好是从根节点到父节点的路径。
我们可以通过两种方法实现,做“加法”或者“减法”。思路是一样的。
代码1:“加法”
import java.util.ArrayList;
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
ArrayList<ArrayList<Integer>> list=new ArrayList<>();
ArrayList<Integer> sublist=new ArrayList<>();
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
if(root==null){
return list;
}
int currSum=0;
findpath(root,target,currSum);
return list;
}
private void findpath(TreeNode root,int target,int currSum){
sublist.add(root.val);
currSum+=root.val;
boolean isleaf=(root.left==null&&root.right==null);
//如果是叶节点,且路径元素和=target
if(isleaf&&currSum==target){
System.out.println(sublist.toString());
list.add(new ArrayList<Integer>(sublist));
}
//不相等,则继续遍历左右子节点
if(root.left!=null){
findpath(root.left,target,currSum);
}
if(root.right!=null){
findpath(root.right,target,currSum);
}
//如果是叶子节点,但是加和和target不相等,则回退到上一个节点
//需要移除当前路径中的这个节点,并将它的值从加和中减掉
sublist.remove(sublist.size()-1);
currSum-=root.val;
return;
}
}
减法思路
树的前序,中序,后序遍历中,只有前序遍历是最先访问根节点的(因此对于先遍历根节点的值,考察的是前序遍历)。
1.首先把当前节点(根节点)添加到路径中,同时用target减去当前节点的值,然后判断如果当前节点到达叶节点,且和为target的值,就把这个路径添加到列表中,否则一直遍历下去。
2.遍历到叶子节点,返回上层节点之前,一定要把最后一个节点从路径中删除。
注:符合条件的路径添加到列表中时,add()方法添加的是引用,如果不是每次new 一个sublist的话,最后list中保存的只是最后一个sublist。
代码2:“减法”
import java.util.ArrayList;
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
ArrayList<ArrayList<Integer>> list=new ArrayList<>();
ArrayList<Integer> path=new ArrayList<>();
//做减法
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
if(root==null){
return list;
}
//每访问到一个节点,就把当前节点添加到路径中,并调整target的值
path.add(root.val);
target-=root.val;
//到了叶子节点,且路径元素加和 与target的值相等
if(target==0&&root.left==null&&root.right==null){
list.add(new ArrayList<Integer>(path));
}
//否则,继续遍历
FindPath(root.left,target);
FindPath(root.right,target);
path.remove(path.size()-1);
return list;
}
}
堆栈实现:
递归的先序遍历。先遍历每一个节点,将先序遍历的每个节点的值相加,相加的结果和target相等,则找到一条路径并打印之。
1.定义currSum用来保存当前访问的路径上的节点之和;定义一个栈用来保存当前路径。为什么用栈?因为栈可以和递归的先序遍历同步。
堆栈实现方法只实现了打印,但并没有实现返回路径的list
public ArrayList<ArrayList<Integer>> FindPath3(TreeNode root,int target){
if(root==null){
return list;
}
LinkedList<Integer> stack=new LinkedList<>();
int currSum=0;
findpath3(root,target,currSum,stack);
return list;
}
private void findpath3(TreeNode root, int target, int currSum, LinkedList<Integer> stack) {
// TODO Auto-generated method stub
currSum+=root.val;
stack.push(root.val);
boolean isleaf=(root.left==null&&root.right==null);
if(currSum==target&&isleaf){
list.add(new ArrayList<>(stack));
Iterator<Integer> it=stack.descendingIterator();//逆序遍历list(stack)
while(it.hasNext()){
System.out.println(it.next()+" ");
}
}
if(root.left!=null){
findpath3(root.left, target, currSum, stack);
}
if(root.right!=null){
findpath3(root.right, target, currSum, stack);
}
stack.pop();//当某节点的左右孩子均为null时,回退。
}
测试代码:
public static void main(String[] args) {
// TODO Auto-generated method stub
TreeNode node10=new TreeNode(10);
TreeNode node5=new TreeNode(5);
TreeNode node12=new TreeNode(12);
TreeNode node4=new TreeNode(4);
TreeNode node7=new TreeNode(7);
PrinTreeDemo.SetSubTreeNode(node10, node5, node12);
PrinTreeDemo.SetSubTreeNode(node5, node4, node7);
FindPathDemo fpd=new FindPathDemo();
ArrayList<ArrayList<Integer>> result=fpd.FindPath3(node10, 22);
System.out.println(result.toString());
}
参考文献: