day21 513.找树左下角的值 112. 路径总和 113. 路径总和ii 106.从中序与后序遍历序列构造二叉树 105.从前序与中序遍历序列构造二叉树

返回值,递归函数什么时候需要返回值?什么时候不需要返回值?这里总结如下三点:

  • 如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值。(113.路径总和ii)
  • 如果需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值。 (这种情况在236. 二叉树的最近公共祖先 (opens new window)中)
  • 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。(112. 路径总和的情况)

513.找树左下角的值

在树的最后一行找到最左边的值

最后一行:深度最大的叶子节点一定是最后一行。

可以使用前序遍历(当然中序,后序都可以,因为本题没有 中间节点的处理逻辑,只要左优先就行),保证优先左边搜索,然后记录深度最大的叶子节点,此时就是树的最后一行最左边的值。

class Solution {
	public int 	Depth = -1;
	public int  value = 0;
    public int findBottomLeftValue(TreeNode root) {
		value = root.val;
		traversal(root , 0);
		return value;
    }

	public void traversal(TreeNode node , int depth){
       if(node == null) return;
	   if(node.left == null && node.right == null){    //终止条件
		   if(depth > Depth ) {
			   value = node.val;
			   Depth = depth;
		   }
	   }
	   if(node.left != null) {      //左优先,先寻找最大深度的最左节点 没有再去右边找
		   depth++;                  
		   traversal(node.left,depth);  //递归操作
		   depth--;                 //回溯操作
	   }
	   if(node.right != null){
		   depth++;
		   traversal(node.right,depth);
		   depth--;
	   }
	}
}

 112. 路径总和

递归函数是有返回值的,如果递归函数返回true,说明找到了合适的路径,应该立刻返回(逐层返回)。

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
		if(root == null) return false;
		return traversal(root , targetSum - root.val);

    }
	public boolean traversal(TreeNode root,int targetSum){
		if(root.left == null && root.right == null && targetSum == 0) return true;
		if(root.left == null && root.right == null && targetSum != 0) return false;
		if(root.left != null){
			targetSum -= root.left.val;
			if(traversal(root.left , targetSum)) return true;
			targetSum += root.left.val;
		}
		if(root.right != null){
			targetSum -= root.right.val;
			if(traversal(root.right , targetSum)) return true;
			targetSum += root.right.val;
		}
		return false;
	}


}

113. 路径总和ii

113.路径总和ii要遍历整个树,找到所有路径,所以递归函数不要返回值!

class Solution {
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
         List<List<Integer>>  res = new ArrayList<>();
		 if(root == null ) return res;
		 List<Integer> path = new ArrayList<>();
         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){
		   if(targetSum - root.val ==0){
			   res.add(new ArrayList<>(path));
		   }
		   return;
	   }
		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); 
		}
	}
}

补充:后序和前序不能构造二叉树

前序中左右  后序左右中   你只可以找到中间节点但是你找不到左右区间的分割点

106.从中序与后序遍历序列构造二叉树

使用递归进行切割:

  • 第一步:如果后序数组大小为零的话,说明是空节点了。

  • 第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。

  • 第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点

  • 第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)

  • 第五步:切割后序数组,切成后序左数组和后序右数组

  • 第六步:递归处理左区间和右区间

首先要切割中序数组,为什么先切割中序数组呢?

切割点在后序数组的最后一个元素,就是用这个元素来切割中序数组的,所以必要先切割中序数组。

中序数组相对比较好切,找到切割点(后序数组的最后一个元素)在中序数组的位置,然后切割

中序数组我们都切成了左中序数组和右中序数组了,那么后序数组就可以按照左中序数组的大小来切割,切成左后序数组和右后序数组。

切割时候会产生四个区间要保证循环不变量(左闭右开 左开右闭  左闭右闭)

class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
      if(postorder.length == 0 || inorder.length ==0)return null;
      return buildHelper(inorder,0,inorder.length,postorder,0,postorder.length);
    }

	public  TreeNode buildHelper(int[] inorder, int inorderStart,int inorderEnd, int[] postorder,int postorderStart,int postorderEnd){
        if(postorderStart == postorderEnd) return null;
		int rootval = postorder[postorderEnd - 1];
		TreeNode root = new TreeNode(rootval);
		int middleIndex;
		for ( middleIndex = inorderStart; middleIndex < inorderEnd; middleIndex++) {
			if(inorder[middleIndex] == rootval){
				break;
			}
		}
		int leftinorderStart = inorderStart;
		int leftinorderEnd = middleIndex;
		int rightinorderStart = middleIndex + 1;
		int rightinorderEnd = inorderEnd;

		int leftPostorderStart = postorderStart;
		int leftPostorderEnd = postorderStart + (middleIndex - inorderStart);
		int rightPostorderStart = leftPostorderEnd;    //左闭右开
		int rightPostorderEnd = postorderEnd - 1;      //最后一个为中间元素直接剔除

		root.left = buildHelper(inorder,leftinorderStart,leftinorderEnd,postorder,leftPostorderStart,leftPostorderEnd);
		root.right = buildHelper(inorder,rightinorderStart,rightinorderEnd,postorder,rightPostorderStart,rightPostorderEnd);

        return root;

	}
}

105.从前序与中序遍历序列构造二叉树

与106基本类似主要重点还是在于区间不变量(左闭右开),在于前序数组的划分(通过中序数组划分完后的长度去划分)

class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
		if(preorder.length == 0 || inorder.length ==0)return null;
		return buildHelper(preorder,0,preorder.length,inorder,0,inorder.length);
	}

	public  TreeNode buildHelper(int[] preorder, int preorderStart,int preorderEnd, int[] inorder,int inorderStart,int inorderEnd){
		if(preorderStart == preorderEnd) return null;
		int rootval = preorder[preorderStart];
		TreeNode root = new TreeNode(rootval);
		int middleIndex;
		for ( middleIndex = inorderStart; middleIndex < inorderEnd; middleIndex++) {
			if(inorder[middleIndex] == rootval){
				break;
			}
		}
		int leftinorderStart = inorderStart;
		int leftinorderEnd = middleIndex;
		int rightinorderStart = middleIndex + 1;
		int rightinorderEnd = inorderEnd;


		int leftpreorderStart = preorderStart + 1;
		int leftpreorderEnd = leftpreorderStart + middleIndex - inorderStart ;
		int rightpreorderStart = leftpreorderEnd ;
		int rightpreorderEnd = preorderEnd;
		
		root.left = buildHelper(preorder,leftpreorderStart,leftpreorderEnd,inorder,leftinorderStart,leftinorderEnd);
		root.right = buildHelper(preorder,rightpreorderStart,rightpreorderEnd,inorder,rightinorderStart,rightinorderEnd);

		return root;

    }
}

  • 19
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值