回溯法5,在二叉树中的应用,递归的栈特性
一.题源
https://leetcode-cn.com/problems/path-sum-ii/
二.递归的栈特性
先看下面的一个链表的例子
public class LinkListTest2 { public static void main(String[] args) { ListNode root = new ListNode(0); ListNode cur = root; for (int i = 1; i < 5; i++) { ListNode node = new ListNode(i); cur.setNext(node); cur = node; } LinkListUtil.displayList(root); List<Integer> list = new ArrayList<>(); backTrace(root,list); for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } } private static void backTrace(ListNode node, List<Integer> list){ if(node==null){ return; } //相当于把数据压入栈 list.add(node.getVal()); System.out.print(list.size()+","); backTrace(node.getNext(),list); //数据出栈 System.out.print(list.size()+","); list.remove(list.size()-1); } }
输出:
0->1->2->3->4->
1,2,3,4,5,5,4,3,2,1,
三.递归的图解分析
从上述结果可以看出,递归方法后面的方法,会随着递归函数的返回,逐一的从栈帧中弹出来并执行。
所以在递归中使用集合,也就形成了压栈和出栈的效果
四.题集的代码
基于上述思想,本题的答案也可以想出来了。
public class Solution { public static void main(String[] args) { TreeNode root = new TreeNode(5); TreeNode left1 = new TreeNode(4); TreeNode left2 = new TreeNode(11); TreeNode left3 = new TreeNode(7); TreeNode right3 = new TreeNode(2); left2.setLeft(left3); left2.setRight(right3); left1.setLeft(left2); TreeNode right1 = new TreeNode(8); TreeNode rleft2 = new TreeNode(13); TreeNode rRight3 = new TreeNode(4); TreeNode rLeft4 = new TreeNode(5); TreeNode rRight4 = new TreeNode(1); right1.setLeft(rleft2); right1.setRight(rRight3); rRight3.setLeft(rLeft4); rRight3.setRight(rRight4); root.setLeft(left1); root.setRight(right1); String p = ""; BTUtils.printAllPath(root,p); System.out.println(p); ArrayUtils.displayArrayList(pathSum(root,22)); } public static List<List<Integer>> pathSum(TreeNode root, int sum) { List<List<Integer>> list = new ArrayList<>(); if(root==null){ return list; } backTrace(list,new ArrayList<>(),root,sum,0); return list; } private static void backTrace(List<List<Integer>> result,List<Integer> list,TreeNode node,int sum,int innerSum){ if(node!=null && node.getLeft()==null && node.getRight()==null){ innerSum+=node.getVal(); if(sum==innerSum){ list.add(node.getVal()); result.add(new ArrayList<>(list)); list.remove(list.size()-1); } return; } if(node==null){ return; } int val = innerSum + node.getVal(); list.add(node.getVal()); if(node.getLeft()!=null){ backTrace(result,list,node.getLeft(),sum,val); } if(node.getRight()!=null){ backTrace(result,list,node.getRight(),sum,val); } list.remove(list.size()-1); } }
输出
5->4->11->7 5->4->11->2 5->8->13 5->8->4->5 5->8->4->1 [ [5,4,11,2], [5,8,4,5] ]
五.题解的图解分析
总之就是在递归方法之后执行的方法,有点像倒过来执行一样,就是从递归的最深处开始,逐一的执行,并返回上一个调用处。
模板如下,
list.add(ele); backTrace(list,1); backTrace(list,2); backTrace(list,3); //..... list.remove();
//不考虑的递归的情况下,可以想象成这样
public void solve(){
Stack<Integer> stack = new Stack<>();
stack.push(ele);
excute(stack);
stack.pop();
}
public void excute(Stack<Integer> stack){
//do something
}
六.总结
要注意的是,最好别在回溯和递归的方法写过于复杂的逻辑,要不然数据很容易计算出错。