题目描述:
输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)
树节点的类定义:
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
通过题目可以知道要通过对树节点的遍历,得到树节点val和等于所输入的值,这就是对树的深度遍历(DFS),一般可以用递归的做法来做,记得以前看过一个DFS的核心模板:
void DFS( 状态参数 ) {
if ( 目的状态 ) {
目的操作
return;
}
for ( 搜索方式(移动) )
{
if ( 合法状态 ) {
操作
标记
DFS( 更新状态参数 )
回溯 ( 还原标记 )
}
}
}
可以参考一下。
但我首先使用的是通过循环来解决问题,代码如下:
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root, int target) {
ArrayList<ArrayList<Integer>> resList = new ArrayList<>();
//防止错误输入
if (root == null) return resList;
//用来做深度优先遍历的栈
Stack<TreeNode> stack = new Stack<>();
//用来记录当前节点是否走过
Set<TreeNode> treeSet = new HashSet<>();
//结果LinkedList<ArrayList<Integer>>集排序链表,排完序后最后再放入ArrayList<ArrayList<Integer>>中
LinkedList<ArrayList<Integer>> sortList = new LinkedList<>();
//首先压入根节点
stack.push(root);
treeSet.add(root);
int sum = root.val;
while (!stack.empty()) {
//找到结果
if (sum == target && stack.peek().right == null && stack.peek().left == null) {
//将结果放入到tmpList中
Stack<TreeNode> tmpStack = (Stack<TreeNode>) stack.clone();
//因为栈是反的,用LinkedList头插
LinkedList<Integer> tmpList = new LinkedList<>();
while (!tmpStack.empty()) {
tmpList.add(0, tmpStack.peek().val);
tmpStack.pop();
}
//LinkedList的插入排序
int insertPos = 0;
while (insertPos < sortList.size()) {
if (tmpList.size() <= sortList.get(insertPos).size())
insertPos++;
}
sortList.add(insertPos, new ArrayList<>(tmpList));
sum -= stack.peek().val;
stack.pop();
continue;
}
//剪枝
if (sum > target) {
sum -= stack.peek().val;
stack.pop();
continue;
}
//表示还未遍历过且不为空
if (!treeSet.contains(stack.peek().left) && stack.peek().left != null) {
treeSet.add(stack.peek().left);
stack.push(stack.peek().left);
sum += stack.peek().val;
continue;
}
//表示还未遍历过且不为空
if (!treeSet.contains(stack.peek().right) && stack.peek().right != null) {
treeSet.add(stack.peek().right);
stack.push(stack.peek().right);
sum += stack.peek().val;
continue;
}
//没有符合上面的情况,回去找
sum -= stack.peek().val;
stack.pop();
}
resList = new ArrayList<ArrayList<Integer>>(sortList);
return resList;
}
这里对于输出的 ArrayList<ArrayList<Integer>> 的输出要求处理的有点繁复,因为栈输出的顺序是逆序的,所以就先把 stack 的输出到 LinkedList 中,然后LinkedList 可以用头插法,不过也可以通过 stack.size() 在 new ArrayList 时确定好大小,然后从后往前插入数据。然后题目要求输入的路径要按路径长度降序来排,也是先用 LinkedList<ArrayList<Integer>>,然后插入时进行插入排序,最后新建ArrayList<ArrayList<Integer>>时再以LinkedList<ArrayList<Integer>> 为参数。
当然不使用 java.util.Stack 直接使用 List 来充当栈来用的话也就不用考虑 Stack 的数据是逆序的问题。
使用递归的方法的话就简洁许多:
private static ArrayList<ArrayList<Integer>> listAll = new ArrayList<ArrayList<Integer>>();
private ArrayList<Integer> list = new ArrayList<Integer>();
//使用递归解决问题
public static ArrayList<ArrayList<Integer>> FindPath2(TreeNode root, int target) {
if(root == null) return listAll;
list.add(root.val);
target -= root.val;
if(target == 0 && root.left == null && root.right == null)
listAll.add(new ArrayList<Integer>(list));
FindPath2(root.left, target);
FindPath2(root.right, target);
//将递归压入的节点删除,还原标记
list.remove(list.size()-1);
return listAll;
}
两种方法都值得学习。