6.11、12 二叉树

平衡二叉树

终止条件:子树为空的时候,空树自然是平衡二叉树了。

返回值:而对于一颗树,它是一个平衡二叉树需要满足三个条件:它的左子树是平衡二叉树,它的右子树是平衡二叉树,它的左右子树的高度差不大于1。

本级递归应该做什么:一级递归有三个节点,root、left、right,需要判断left和right是不是平衡的且以left为根节点和以right为根节点的树高度差是否<=1,如果是,则返回true。

 public boolean isBalanced(TreeNode root) {
    	  if(root==null)return true;
    	  //一层递归做的事情
    	  if(isBalanced(root.left) && isBalanced(root.right) && Math.abs(dfs(root.left)-dfs(root.right))<=1)return true;
    	  else return false;
      }
    
      public int dfs(TreeNode root) {//求树的高度
		if(root==null) {
			return 0;
		}
		//返回值是左子树和右子树最大高度值+根节点应该占得高度(1)
		return Math.max(dfs(root.left), dfs(root.right))+1;
	}

二叉树的所有路径

很简单的一道题,但是很多细节

可以利用new每次传参都开一个new StringBuilder(),维护每个节点的路径。

      public List<String> binaryTreePaths(TreeNode root) {
    	  List<String> res=new LinkedList<>();
    	  if(root==null)return res;
    	  backtrack(root, res, new StringBuilder());
    	  return res;
      }
      
      public void backtrack(TreeNode root, List<String> res, StringBuilder result) {
        if(root==null)return;
        //只要这个结点不是null就加入路径中去
        result.append(root.val);
		if(root.left==null && root.right==null) {
			res.add(result.toString());
			return;
		}
		if(root.left!=null) {
			//为什么要new而不用恢复现场去回溯?
			//因为每次加入一个新节点值时,需要连接一个"->",如果remove的话只能去除一个值,而"->"是两个字符
			//所以每次new一下来维护一个根节点的路径
		backtrack(root.left, res, new StringBuilder(result).append("->"));
		}
		if(root.right!=null) {
		backtrack(root.right, res, new StringBuilder(result).append("->"));
		}
	}

左叶子和

判断左叶子的条件:为叶子结点&&是左结点

flag来标记是否为左结点,根节点的左、右叶子都是null来判断是否为叶子结点

      int sum=0;
      public int sumOfLeftLeaves(TreeNode root) {
    	  if(root==null)return 0;
    	  backtrack(root, false);
    	  return sum;
      }
      public void backtrack(TreeNode root, boolean flag) {
		if(root==null)return;
		if(root.left==null && root.right==null && flag) {
			sum+=root.val;
			return;
		}

		backtrack(root.left, true);
		backtrack(root.right, false);
	}

只关注终止条件、参数、返回值:

 

 参数:当前结点的左、右节点

返回值:左、右子树的左叶子结点和+当前本结点的左叶子结点,若有就是if里面赋值的sum,若没有就是0;

      public int sumOfLeftLeaves(TreeNode root) {
    	  if(root==null)return 0;
          int sum=0;
    	  if(root.left!=null && root.left.left==null && root.left.right==null) {
    		  sum=root.left.val;
    	  }
    	  return sumOfLeftLeaves(root.left)+sumOfLeftLeaves(root.right)+sum;
      }

路径总和

注意:

这个什么时候+sum?

		dfs(root.left, targetSum, sum+root.val);
//不能这样,因为你传入的参数是此根节点的左节点了,可是sum和还没加到左节点的值
//所以这个+的操作应该放在判断此结点是否为null后面,如果不为null,就应该加上这个结点

 前两版本的代码差别在何时+sum上,都能ac

      boolean flag=false;
      public boolean hasPathSum(TreeNode root, int targetSum) {
    	  if(root==null) return false;
    	  dfs(root, targetSum, 0);
    	  return flag; 		  
    	  }
    	  
      public void dfs(TreeNode root, int targetSum, int sum) {
		if(root==null) return;
        sum+=root.val;
		if(root.left==null && root.right==null) {
			if(sum==targetSum)flag=true;
		}
		dfs(root.left, targetSum, sum);
		dfs(root.right, targetSum, sum);
	}
boolean flag=false;
      public boolean hasPathSum(TreeNode root, int targetSum) {
    	  if(root==null) return false;
    	  dfs(root, targetSum, root.val);
    	  return flag; 		  
    	  }
    	  
      public void dfs(TreeNode root, int targetSum, int sum) {
		if(root==null) return;
		if(root.left==null && root.right==null) {
			if(sum==targetSum)flag=true;
		}
        if(root.left!=null)
		dfs(root.left, targetSum, sum+root.left.val);
        if(root.right!=null)
		dfs(root.right, targetSum, sum+root.right.val);
	}

更简洁的版本:

传入参数:每层根节点和此时根节点所需要的sum值

终止条件:结点为空

返回值:此结点的左子树-该节点的值是否和为target或者此结点的右子树-该节点的值是否和为targe

构建二叉树(中序+后序)

思想:

看一下中序和后序有什么特点,中序[9,3,15,20,7] ,后序[9,15,7,20,3]

有如下特征:

  1. 后序中右起第一位3肯定是根结点,我们可以据此找到中序中根结点的位置rootin
  2. 中序中根结点左边就是左子树结点,右边就是右子树结点,即[左子树结点,根结点,右子树结点],我们就可以得出左子树结点个数;
  3. 后序中结点分布应该是:[左子树结点,右子树结点,根结点]
  4. 根据前一步确定的左子树个数,可以确定后序中左子树结点和右子树结点的范围;
  5. 如果我们要前序遍历生成二叉树的话,下一层递归应该是:
    • 左子树:root->left = pre_order(中序左子树范围,后序左子树范围,中序序列,后序序列);
    • 右子树:root->right = pre_order(中序右子树范围,后序右子树范围,中序序列,后序序列);
  6. 每一层递归都要返回当前根结点root

注意:一定要先构建左子树再构建右子树

每一层要做的事情就是找出根节点,再找出左子树、右子树的数组范围,最后分别构建左右子树。

      public TreeNode buildTree(int[] inorder, int[] postorder) {
    	  return build(0, inorder.length-1, 0, postorder.length-1, inorder, postorder);
      }
      public TreeNode build(int inleft, int inright, int postleft, int postright, int[] inorder, int[] postorder) {
		if(inleft>inright)return null;
		TreeNode root=new TreeNode(postorder[postright]);
		int point=0;
//记录根节点
		for(int i=0; i<=inright; i++) {
			if(postorder[postright]==inorder[i]) {
				point=i;
				break;
			}
		}
//递归求解左子树和右子树
	root.left=build(inleft, point-1, postleft, postleft+point-1-inleft, inorder, postorder);
	root.right=build(point+1, inright, postleft+point-inleft, postright-1, inorder,postorder);
	return root;
	}

构建二叉树(前序+中序)

      public TreeNode buildTree(int[] preorder, int[] inorder) {
    	  return build(0, preorder.length-1, 0, inorder.length-1, preorder, inorder);
      }
      public TreeNode build(int preleft, int preright, int inleft, int inright, int[] preorder, int[] inorder) {
		if(inleft>inright)return null;
		TreeNode root=new TreeNode(preorder[preleft]);
		int point=0;
		for(int i=inleft; i<=inright; i++) {
			if(preorder[preleft]==inorder[i]) {
				point=i;
				break;
			}
		}
		//中左右  左中右
		root.left=build(preleft+1, preleft+point-inleft, inleft, point-1, preorder, inorder);
		root.right=build(preleft+point-inleft+1, preright, point+1, inright, preorder, inorder);
		return root;
	}

为什么构建二叉树一定要有中序?因为前序、后序只能只能找出根节点,而不能判断根节点的左子树、右子树是哪些数组,而中序恰好可以解决这个问题,在中序中找到根节点后,可以找出左右子树,相当于确定了树的形状。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值