找树左下角的值
题目的核心问题:在树的最后一行找到最左边的值。
递归思路:要找到树的最后一行最左边的值,不能单纯的一路向左递归。要先找到最后一行。只要以左节点优先遍历的递归均可,这里选用先序遍历。遍历找到叶子节点,记录最大深度,每找到一个叶子节点,比较其深度和最大深度,如果其深度大于最大深度,更新最大深度和结果。最后返回结果即可。所以要定义两个全局变量:最大深度和结果。
1、传入参数:传入根节点和层数,有全局变量所以无需返回值;
2、终止条件:节点为空,返回;
3、递归函数:如果节点为叶子节点,更新最大深度和结果。如果节点的左孩子存在,深度++,递归左孩子,深度–(回溯);如果节点的右孩子存在,深度++,递归右孩子,深度–(回溯)。
代码如下:
class Solution {
int maxDeep=-1;
int result=0;
public int findBottomLeftValue(TreeNode root) {
result=root.val;
findLeftValue(root,0);
return result;
}
public void findLeftValue(TreeNode root,int deep){
if(root==null) return;
if(root.left==null && root.right==null){
if(deep>maxDeep){
maxDeep=deep;
result=root.val;
}
}
if(root.left!=null){
deep++;
findLeftValue(root.left,deep);
deep--;
}
if(root.right!=null){
deep++;
findLeftValue(root.right,deep);
deep--;
}
}
}
迭代思路:层次遍历。使用层次遍历只需要找到最后一行,输出第一个元素的值即可。
代码如下:
class Solution {
public int findBottomLeftValue(TreeNode root) {
Queue<TreeNode> queue=new LinkedList<>();
queue.offer(root);
int result=root.val;
while(!queue.isEmpty()){
int size=queue.size();
for(int i=0;i<size;i++){
TreeNode node=queue.poll();
if(i==0) result=node.val;//记录每一层的第一个元素
if(node.left!=null) queue.offer(node.left);
if(node.right!=null) queue.offer(node.right);
}
}
return result;
}
}
路径总和
递归函数什么时候需要返回值?什么时候不需要返回值?这里总结如下三点:
- 如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值。(这种情况是本文介绍的113.路径总和ii)
- 如果需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值。 (这种情况我们在236. 二叉树的最近公共祖先 (opens new window)中介绍)
- 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。(这种情况是本文介绍的112.路径总和)
112. 路径总和
这个题目一眼递归+回溯。只需要确定是否存在这样一条路径满足条件,返回boolean类型
选择先序遍历,递归三部曲:
1、传入参数:root根节点,传入目标值target。不要遍历整棵树,所以递归函数需要返回值,返回值boolean类型。使用原函数即可。
2、终止条件:如果节点为空,直接返回false(根节点为空的情况);节点为叶子节点(左右孩子为空),返回target==root.val;
3、递归逻辑:如果左孩子存在,target减去节点值,如果遍历左孩子的结果为true直接返回true,target加上节点值(回溯);如果右孩子存在,target减去节点值,如果遍历右孩子的结果为true直接返回true,target加上节点值(回溯);
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
if(root==null) return false;//根节点为空的情况
if(root.left==null && root.right==null) return targetSum==root.val;
boolean left=hasPathSum(root.left,targetSum-root.val);
boolean right=hasPathSum(root.right,targetSum-root.val);
return left || right;
}
}
113. 路径总和 II
需要返回所有符合条件的路径。
递归三部曲:
1、传入参数:根节点root,目标值target,结果列表result,路径列表path;直接加入result表中,无返回值
2、终止条件:当节点为叶子节点且目标值减去当前节点的值结果是0时,将该路径加入result表中。否则直接return。
3、递归函数逻辑:首先将遍历的节点加入path中,如果左孩子存在,递归调用作节点,并传入目标值减去当前节点值的结果作为目标值(包含了回溯),path中移除最后一个节点(回溯);右孩子同理
class Solution {
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
List<List<Integer>> result=new ArrayList<>();
if(root==null) return result;
List<Integer> path=new LinkedList<>();
everyPath(root,targetSum,result,path);
return result;
}
public void everyPath(TreeNode root,int targetSum, List<List<Integer>> result,List<Integer> path){
path.add(root.val);
if(root.left==null && root.right==null){
if(targetSum==root.val){
result.add(new ArrayList<>(path));
}
return;
}
if(root.left!=null){
everyPath(root.left,targetSum-root.val,result,path);
path.remove(path.size()-1);
}
if(root.right!=null){
everyPath(root.right,targetSum-root.val,result,path);
path.remove(path.size()-1);
}
}
}
从中序与后序遍历序列构造二叉树
106.从中序与后序遍历序列构造二叉树
根据中序与后序遍历序列构造二叉树的步骤:
第一步:如果数组大小为零的话,说明是空节点了。
第二步:如果不为空,那么取后序数组最后一个元素(根节点)作为节点元素。
第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
第五步:根据中序数组的大小切割后序数组,切成后序左数组和后序右数组
第六步:递归处理左区间和右区间
那么递归的三部曲分别是什么呢?
由于需要分割数组,所以我们使用HsahMap来记录中序序列的元素和位置。
1、传入参数:中序序列,中序序列的开始和结束位置,后序序列,后序序列的开始和结束位置。返回值为树的节点类型。
2、终止条件:当开始位置大于等于结束位置时,说明数组中不存在元素,return null。
3、递归函数逻辑:首先找到后序数组最后一个元素,在哈希表中找到这个元素在中序序列中的下标,分割左右子树,同时根据中序序列左右子树的元素个数对后序序列进行分割。为后序数组最后一个元素建立节点,为根节点,递归调用函数找到该节点的左孩子和右孩子。
代码:
class Solution {
HashMap<Integer,Integer> map;
public TreeNode buildTree(int[] inorder, int[] postorder) {
map=new HashMap<>();
for(int i=0;i<inorder.length;i++){
map.put(inorder[i],i);
}
return buildNewTree(inorder,0,inorder.length,postorder,0,postorder.length);
}
public TreeNode buildNewTree(int[] inorder,int inStart,int inEnd,int[] postorder,int postStart,int postEnd){
if(inStart>=inEnd || postStart>=postEnd){
return null;
}
int index=map.get(postorder[postEnd-1]);
int len=index-inStart;
TreeNode root=new TreeNode(inorder[index]);
root.left=buildNewTree(inorder,inStart,index,postorder,postStart,postStart+len);
root.right=buildNewTree(inorder,index+1,inEnd,postorder,postStart+len,postEnd-1);
return root;
}
}
注意:在写递归函数时,一定要注意左闭右开区间。
105.从前序与中序遍历序列构造二叉树
同理,前序的第一个元素为中间节点,可以对中序遍历序列进行分割。
代码如下:
class Solution {
HashMap<Integer,Integer> map;
public TreeNode buildTree(int[] preorder, int[] inorder) {
map=new HashMap<>();
for(int i=0;i<inorder.length;i++){
map.put(inorder[i],i);
}
return buildNewTree(preorder,0,preorder.length,inorder,0,inorder.length);
}
public TreeNode buildNewTree(int[] preorder,int preStart,int preEnd,int[] inorder,int inStart,int inEnd){
if(preStart>=preEnd || inStart>=inEnd){
return null;
}
int index=map.get(preorder[preStart]);
TreeNode root=new TreeNode(inorder[index]);
int lenOfLeft=index-inStart;
root.left=buildNewTree(preorder,preStart+1,preStart+1+lenOfLeft,inorder,inStart,index);
root.right=buildNewTree(preorder,preStart+1+lenOfLeft,preEnd,inorder,index+1,inEnd);
return root;
}
}