public class FindBottomLeftTreeValue { private int Deep = -1;//遍历的所以深度的最大值 private int result = 0; public int findBottomLeftValue(TreeNode root){ result = root.val; findLeftValue(root, 0); return result; } private void findLeftValue(TreeNode root, int deep){//这里deep是当前遍历的深度 if(root == null)return; if(root.left == null && root.right == null){//终止条件,叶子节点==null if(deep > Deep){ Deep = deep; result = root.val;//记录当前节点的数值 } } if(root.left != null){ deep++; findLeftValue(root.left, deep); deep--;//回溯就在递归函数的下面 } if(root.right != null){ deep++; findLeftValue(root.right, deep); deep--; } } }
思路:
这道题我们要遍历从根节点到叶子节点的的路径看看总和是不是目标和。
1.确定递归函数的参数和返回值:
参数:二叉树的根节点,目标值,
再来看返回值,递归函数什么时候需要返回值?什么时候不需要返回值?这里总结如下三点:
- 如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值。(这种情况就是本文下半部分介绍的113.路径总和ii)
- 如果需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值。 (这种情况我们在236.最小公共祖先中介绍)
- 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。(本题的情况)
而本题我们要找一条符合条件的路径,所以递归函数需要返回值,及时返回,那么返回类型是什么呢?
遍历的路线,并不要遍历整棵树,所以递归函数需要返回值,可以用bool类型表示。
public boolean haspathSum(TreeNode root, int targetSum){ if(root == null) return false; return path(root, targetSum, 0); } public boolean path(TreeNode node, int targetSum, int Sum){ //递归终止条件 if(node.left == null && node.right == null){ Sum +=node.val;// //找到了目标值 if(Sum == targetSum){ return true; } else{ return false; } } if(node.left != null){ Sum += node.val; if(path(node.left, targetSum, Sum)) return true;//判断左方向是否有符合条件的,需要向上去返回 Sum-=node.val; } if(node.right !=null){ Sum += node.val; if(path(node.right, targetSum,Sum)) return true;//函数有返回值,相当于告诉我们右子树 或者右方向的 是否有符合条件的 } return false; }
思路:
113.路径总和ii要遍历整个树,找到所有路径,所以递归函数不要返回值!
import java.util.ArrayList; import java.util.LinkedList; import java.util.List; public class PathSum2 { public List<List<Integer>> pathSum(TreeNode root, int targetSum){ List<List<Integer>> res = new ArrayList<>(); if (root == null) return res; List<Integer> path = new LinkedList<>(); preorderdfs(root, targetSum, res, path); return res; } public void preorderdfs(TreeNode root, int targetSum, List<List<Integer>> res, List<Integer> path){ path.add(root.val); // 遇到了叶子节点 if(root.left == null && root.right == null){ // 找到了和为 targetsum 的路径 if(targetSum - root.val == 0){ res.add(new ArrayList<>(path)); } return;// 如果和不为 targetsum,返回 } if(root.left != null){ preorderdfs(root.left, targetSum - root.val, res, path); path.remove(path.size() - 1);// 回溯 } if(root.right != null){ preorderdfs(root.right, targetSum - root.val, res, path); path.remove(path.size()- 1);// 回溯 } } }
从中序与后序遍历序列构造二叉树
说到一层一层切割,就应该想到了递归。
来看一下一共分几步:
-
第一步:如果数组大小为零的话,说明是空节点了。
-
第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
-
第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
-
第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
-
第五步:切割后序数组,切成后序左数组和后序右数组
-
第六步:递归处理左区间和右区间
import java.util.Arrays; public class ConstructBinaryTreefromInorderandPostorderTraversal { public TreeNode buildTree(int[] inorder, int[] postorder){ return tree(inorder, postorder); } public TreeNode tree(int[] inorder, int[] postorder){ //1.递归终止条件:后序的长度为0 if(postorder.length == 0)return null; //2.后序遍历中的最后一个元素就是根节点 int rootValue = postorder[postorder.length - 1]; //此时就可以定义一个节点了 TreeNode root = new TreeNode(rootValue); //特殊情况只有一个节点 if(postorder.length == 1) return root; //3.寻找中序数组位置作为切割点 int index = 0; for(int i = 0; i < inorder.length; i++){ //中序数组里找到中点 if(inorder[i] == rootValue){ index = i; break; } } //4.切中序数组-左中序,右中序 int[] leftIn = Arrays.copyOfRange(inorder, 0, index);//左中序 int[] rightIn = Arrays.copyOfRange(inorder, index + 1, inorder.length);//右中序 //5.切后序数组 //根据左中序数组 去构建左后序数组 int[] leftPost = new int[leftIn.length]; int[] rightPost = new int[rightIn.length]; for(int i = 0; i < leftIn.length; i++){ leftPost[i] = postorder[i]; } //System.out.println("左后序"+Arrays.toString(leftPost)); for(int i = 0; i < rightIn.length; i++){ rightPost[i] = postorder[index+ i]; } // System.out.println("右后序"+Arrays.toString(rightPost)); //6.递归处理左区间和右区间 root.left = tree(leftIn, leftPost); root.right = tree(rightIn, rightPost); return root; } }
import java.util.Arrays; public class ConstructBinaryTreefromPreorderandInorderTraversal { public TreeNode buildTree(int[] preorder, int[] inorder){ return tree(preorder, inorder); } public TreeNode tree(int[] preorder, int[] inorder){ //中左右,左中右 //终止条件 if(preorder.length == 0) return null; int rootValue = preorder[0];//前序遍历的第一个元素就是根节点 TreeNode root = new TreeNode(rootValue); //根据根节点去中序中切分 int index =0; for(int i = 0; i < inorder.length; i++){ if(inorder[i] == root.val){ index = i; break; //找到了 下标 即第一轮 3的位置 下标为1 } } //根据下标去切分左中序 和右中序 int[] leftIn = Arrays.copyOfRange(inorder, 0, index); //System.out.println("左中序"+Arrays.toString(leftIn)); int[] rightIn = Arrays.copyOfRange(inorder, index + 1, inorder.length); //System.out.println("右中序"+Arrays.toString(rightIn)); //根据左中序 和右中序 去切分前序数组 int[] leftPre = new int[leftIn.length]; int[] rightPre = new int[rightIn.length]; for(int i = 0; i < leftIn.length; i++){ leftPre[i] = preorder[i + 1]; } //System.out.println("左前序"+Arrays.toString(leftPre)); for(int i =0; i< rightIn.length; i++){ rightPre[i] =preorder[leftIn.length+1+i]; } //System.out.println("右前序"+Arrays.toString(rightPre)); root.left = tree(leftPre, leftIn); root.right = tree(rightPre, rightIn); return root; } }