算法与数据体系课笔记之-12. 二叉树类型判断与树DP思路总结(进行中)

12. 二叉树类型判断与树DP思路总结 总览

笔记思维导图链接

算法与数据结构思维导图

参考左程云体系算法课程笔记
参考慕课网算法体系课程笔记

常见题目汇总:

1. 完全二叉树的判断

1)完全二叉树定义

img

  • 要么所有子树都是满的,即满二叉树
  • 要么最底层从左往右都是满的
    • 即,某个节点没有左子节点,那么肯定也不能有右子节点
    • 除了最底层可能没有排满的外,其余每层节点都满了,且最后一层的特点是从左往右排

2)完全二叉树的判断题

题意
  • 给定一棵二叉树的头节点head,返回这颗二叉树中是不是完全二叉树
题解
  • 1.对完全二叉树的判断关键是倒数第二层,找到左右孩子节点不满的位置开始判断
    • 因此,层序遍历更方便观察是否为倒数第二层和最低层的情况
    • 因此,需要设置变量标志这个父亲节点
  • 2.当找到这个不满的父节点后,分两种情况判断
    • 第一种是,要判断这个父亲节点是否在倒数第二层,即同一层和下一层所有节点必须是叶子节点,否则一票否决
    • 第二种是,要判断是否出现左为null,右不为null的情况,也一票否决
  • 3.每层遍历判断完后,要将子节点放入队列,继续下一层的遍历判断
	public static boolean isCBT1(Node root) {
		if(root == null) return true;
		
		Queue<Node> queue = new LinkedList<>();
		boolean leaf = false; // 第一个不满节点标志,后面都为叶子节点
		Node left = null;
		Node right = null;
		queue.add(root); // 层序遍历前提,先有根节点
		while(!queue.isEmpty()) {
			Node cur = queue.poll();
			left = cur.left;
			right = cur.right;
			if((leaf && (left != null || right != null)) // 遇到不满节点,后面节点不是叶子节点
					|| (left == null && right != null)){ // 左子树不满,右子树满
				return false;
			}
			if(left != null) queue.add(left);
			if(right != null) queue.add(right);
			if(left == null || right == null) leaf = true;
		}
		return true;
	}
复杂度分析
  • 与层序遍历的复杂度相同,均为O(n)

2. 平衡二叉树的判断

1)平衡二叉树定义

img

平衡二叉树,又称AVL树,具有以下性质:

  • 可以是一颗空树
  • 左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一颗平衡二叉树

2)使用树的动态规划去判断

题意
  • 给定一棵二叉树的头节点head,返回这颗二叉树中是不是平衡二叉树
题解
树的动态规划思想:
  • 先将左右子问题的解拿到,然后用左右子问题的解来处理当前节点的问题

  • 当前问题的解继续向上提供,直到汇总到根节点,判断所有信息

  • 实质上是一个后续遍历过程,只是在遍历过程中,需要的信息进行封装传递

    img
解题步骤:
  • 1.先将需要的信息进行封装

    • 判断是否为平衡二叉树,需要节点的高度,节点本身是否是平衡二叉树两个信息
  • 2.处理用子问题提供的信息解决父问题的逻辑,即f(n)=f(n + 1)

    • 递归终止条件,节点为null,null的高度和本身信息可以确定,可以直接返回信息结果
    • 拿到左右子节点提供的信息
    • 处理当前节点的两个信息
      • 节点高度,是左右节点最大高度+1
      • 是否为平衡二叉树,排除法,三种情况不是平衡二叉树,排除后,就是平衡二叉树
        • 左不是平衡二叉树
        • 右不是平衡二叉树
        • 左子树与右子树的高度差绝对值超过1
  • 3.最后将当前节点处理的信息返回给上级,进一步作为子信息处理上级的问题

    	public static boolean isBalanced2(Node root) {
    		if(root == null) return true;
    		
    		return dfs(root).isBalanced;
    	}
    	
    	public static class Info{
    		boolean isBalanced;
    		int height;
    		public Info(boolean isBalanced, int height) {
    			super();
    			this.isBalanced = isBalanced;
    			this.height = height;
    		}				
    	}
    	
    	public static Info dfs(Node node) {
    		if(node == null) return new Info(true, 0);
    		
    		Info leftInfo = dfs(node.left);
    		Info rightInfo = dfs(node.right);
    		
    		int height = Math.max(leftInfo.height, rightInfo.height) + 1;
    		boolean isBalanced = true;
    		if(!leftInfo.isBalanced) isBalanced = false;
    		if(!rightInfo.isBalanced) isBalanced = false;
    		if(Math.abs(leftInfo.height - rightInfo.height) > 1)  isBalanced = false;
    		
    		return new Info(isBalanced, height);
    	}
    

3. 搜索二叉树的判断

1)搜索二叉树定义

img
  • 二叉树是一个有序树,中序遍历结果是从小到大的排序结果

    • 左子树上所有节点的值均小于根节点的值
    • 右子树上所有节点的值均大于根节点的值
    • 左右子树也均是二叉搜索树
  • 二叉搜索树默认是不包含相同节点,如果一定有,一般处理是将相同节点压缩在一个节点挂在树上

    img

2)使用树的动态规划去判断

题意
  • 给定一棵二叉树的头节点head,返回这颗二叉树中是不是搜索二叉树
题解
  • 1.先将需要的信息进行封装
    • 判断是否为平衡二叉树,需要考虑的情况
      • 左右两颗子树均是搜索树,
      • 且左子树的最大值max小于根节点,右子树最小值min大于根节点的值
    • 因此,需要子节点(每个节点)提供的信息有
      • 以该节点为根节点的分支树是否是二叉搜索树 isBST
      • 该子树中的最小值 min
      • 该子树中的最大值 max
  • 2.处理用子问题提供的信息解决父问题的逻辑,即f(n)=f(n + 1)
    • 递归终止条件,节点为null,null的信息不好确定,直接返回null,在上级中处理null的情况
    • 拿到左右子节点提供的信息
    • 处理当前节点的三个信息
      • 最小值min,最大值max,根节点分别与左右子树的min,max比较,得出比较值
      • 是否为二叉搜索树,排除法,三种情况不是平衡二叉树,排除后,就是搜索二叉树
        • 左不是搜索二叉树
        • 右不是搜索二叉树
        • 左子树的最大值大于根,右子树的最小值小于根
  • 3.最后将当前节点处理的信息返回给上级,进一步作为子信息处理上级的问题
	public static boolean isBST2(Node root) {
		if (root == null)
			return true;

		return dfs(root).isBST;
	}

	public static class Info {
		boolean isBST;
		int min;
		int max;

		public Info(boolean isBST, int min, int max) {
			super();
			this.isBST = isBST;
			this.min = min;
			this.max = max;
		}
	}

	public static Info dfs(Node node) {
		if (node == null)
			return null;

		Info leftInfo = dfs(node.left);
		Info rightInfo = dfs(node.right);

		boolean isBST = true;
		int min = node.value;
		int max = node.value;
		if (leftInfo != null) {
			min = Math.min(min, leftInfo.min);
			if (!leftInfo.isBST || leftInfo.max >= node.value)
				isBST = false;
		}
		if (rightInfo != null) {
			max = Math.max(max, rightInfo.max);
			if (!rightInfo.isBST || rightInfo.min <= node.value)
				isBST = false;
		}

		return new Info(isBST, min, max);
	}

4. 二叉树两个节点最大距离

题意

  • 给定一棵二叉树的头节点head,任何两个节点之间都存在距离,
    返回整棵二叉树的最大距离

    img

题解

  • 1.先将需要的信息进行封装

    • 判断是否为平衡二叉树,需要考虑的情况:分为与头节点X有关,与X无关两种

      • 1.与X无关,最大距离在左最大距离和右子树最大距离中的一个

      • 2.与X有关,最大距离经过X节点,则最终距离为X左树的高度+右树的高度+1

        img
    • 因此,需要子节点(每个节点)提供的信息有

      • 没颗树的最大距离

      • 树的高度

        img
  • 2.处理用子问题提供的信息解决父问题的逻辑,即f(n)=f(n + 1)

    • 递归终止条件,节点为null,null的信息好确定,最大距离为0,高度为0
    • 拿到左右子节点提供的信息
    • 处理当前节点的两个信息
      • 树的高度:左树高度与右树高度的最大值加1
      • 树的最大距离:与X是否有关分为三种情况,取三种情况的最大值
        • 与X无关
          • 左树的最大距离
          • 右树的最大距离
        • 与X有关
          • 左树的高度+右树的高度+ 1
  • 3.最后将当前节点处理的信息返回给上级,进一步作为子信息处理上级的问题

	public static int maxDistance2(Node root) {
		if (root == null)
			return 0;
		return dfs(root).maxDistance;
	}

	public static class Info {
		int maxDistance;
		int height;

		public Info(int maxDistance, int height) {
			super();
			this.maxDistance = maxDistance;
			this.height = height;
		}
	}

	public static Info dfs(Node node) {
		if (node == null)
			return new Info(0, 0);

		Info leftInfo = dfs(node.left);
		Info rightInfo = dfs(node.right);

		int height = Math.max(leftInfo.height, rightInfo.height) + 1;
		int p1 = leftInfo.maxDistance;
		int p2 = rightInfo.maxDistance;
		int p3 = leftInfo.height + rightInfo.height + 1;
		int maxDistance = Math.max(p1, Math.max(p2, p3));

		return new Info(maxDistance, height);
	}

5. 满二叉树的判断

1)满二叉树定义

img
  • 二叉树每一层节点都是满的

    • 即一颗二叉树要么最底层是叶子节点,要么必须有两个子节点
  • 高度h和节点个数N满足关系:

    • 2 ^ h - 1 = N

2)使用树的动态规划去判断

题意
  • 给定一棵二叉树的头节点head,返回这颗二叉树中是不是满二叉树
题解
  • 1.先将需要的信息进行封装
    • 判断是否为平衡二叉树,需要考虑的情况
      • 节点个数与高度的关系
    • 因此,需要子节点(每个节点)提供的信息有
      • 节点个数
      • 树的高度
  • 2.处理用子问题提供的信息解决父问题的逻辑,即f(n)=f(n + 1)
    • 递归终止条件,节点为null,null信息好确定,节点个数为0, 高度为0
    • 拿到左右子节点提供的信息
    • 处理当前节点的三个信息
      • 节点个数:为左右两个子节点个数之和再加1
      • 节点高度:为左右节点高度最大值加1
  • 3.最后将当前节点处理的信息返回给上级,进一步作为子信息处理上级的问题
	// 第一种方法
	// 收集整棵树的高度h,和节点数n
	// 只有满二叉树满足 : 2 ^ h - 1 == n
	public static boolean isFull1(Node root) {
		if (root == null)
			return true;

		Info info = dfs(root);
		return (1 << info.height) - 1 == info.size;
	}

	public static class Info {
		int size;
		int height;

		public Info(int size, int height) {
			super();
			this.size = size;
			this.height = height;
		}
	}

	public static Info dfs(Node node) {
		if (node == null)
			return new Info(0, 0);

		Info leftInfo = dfs(node.left);
		Info rightInfo = dfs(node.right);

		int size = leftInfo.size + rightInfo.size + 1;
		int height = Math.max(leftInfo.height, rightInfo.height) + 1;

		return new Info(size, height);
	}

6. 二叉树中最大的二叉搜索子树大小

题意

  • 给定一棵二叉树的头节点head,
    返回这颗二叉树中最大的二叉搜索子树的头节点

例如:

在这里插入图片描述
  • 最大的搜索二叉子树的大小是4

题解

  • 1.先将需要的信息进行封装
    • 判断是否为平衡二叉树,需要考虑的情况:X做头,和X不做头两种情况
      • 1.X不做头节点
        • 整颗树不是搜索树,左右子树里面有
        • 最大搜索树个数是左子树里面的最大搜索树个数
        • 最大搜索树个数是右子树里面的最大搜索树个数
      • 2.X做头节点
        • 明整颗树都是二叉搜索树,并且要计算出整颗树的节点个数
        • 左树是搜索树,且左树的最大值小于X的值
        • 右树是搜索树,且右树的最小值大于X的值
        • 返回的结果是左子树个数与右子树个数相加再加1
    • 因此,需要子节点(每个节点)提供的信息有
      • 总的节点个数allSize
      • 最大搜索树的个数:maxBSTSubtreeSize,与allSize判断可确定是否整颗树是搜索树
      • 最小值min
      • 最大值max
  • 2.处理用子问题提供的信息解决父问题的逻辑,即f(n)=f(n + 1)
    • 递归终止条件,节点为null,null信息不好确定,交给上级处理
    • 拿到左右子节点提供的信息
    • 处理当前节点的四个信息
      • 总的节点个数allSize,左右节点总数相加+ 1
      • 最大搜索树的个数
        • X不做头节点,就是左右子树最大搜索树个数中的最大一个
        • X做头节点,就是左右子树累加再加1
          • 前提是先判断X能否做头,即整颗树是否是搜索树
      • 最小值min,与左子树比较
      • 最大值max,与右子树比较
  • 3.最后将当前节点处理的信息返回给上级,进一步作为子信息处理上级的问题
	public static int maxSubBSTSize2(Node root)  {
		if(root == null) return 0;
		return dfs(root).maxSubBSTSize;
	}
	
	public static class Info {
		int maxSubBSTSize;
		int allSize;
		int min;
		int max;
		public Info(int maxSubBSTSize, int allSize, int min, int max) {
			super();
			this.maxSubBSTSize = maxSubBSTSize;
			this.allSize = allSize;
			this.min = min;
			this.max = max;
		}
	}
	
	private static Info dfs(Node node) {
		if(node == null) return null;
		
		Info leftInfo = dfs(node.left);
		Info rightInfo = dfs(node.right);
		
		int allSize = 1; // 注意不能直接累加左右子树,要判null
		int min = node.value;
		int max = node.value;
		// 为了求maxSubBSTSize,分为三种情况
		// 前两种情况是X不做头节点的情况,分别求左右子树的最大个数
		int p1 = -1;
		int p2 = -1;
		int p3 = -1;
				
		if(leftInfo != null) {
			min = Math.min(min, leftInfo.min);
			max = Math.max(max, leftInfo.max);
			allSize += leftInfo.allSize;
			p1 = leftInfo.maxSubBSTSize;
		}
		if(rightInfo != null) {
			max = Math.max(max, rightInfo.max);
			min = Math.min(min, rightInfo.min);
			allSize += rightInfo.allSize;
			p2 = rightInfo.maxSubBSTSize;
		}
		
		//第三种情况,要判断整颗树是不是二叉搜索树,否则没有第三章情况的值
		boolean leftBST = leftInfo == null ? true : 
			leftInfo.maxSubBSTSize == leftInfo.allSize;
		boolean rightBST = rightInfo == null ? true : 
			rightInfo.maxSubBSTSize == rightInfo.allSize;
		if(leftBST && rightBST) {
			boolean leftMaxLessX = leftInfo == null ? true : 
				(leftInfo.max < node.value);
			boolean rightMinMoreX = rightInfo == null ? true :
				(rightInfo.min > node.value);
			if(leftMaxLessX && rightMinMoreX) {
				int leftSize = leftInfo == null ? 0 : leftInfo.allSize;
				int rightSize = rightInfo == null ? 0 : rightInfo.allSize;
				p3 = leftSize + rightSize + 1;
			}
		}
		int maxSubBSTSize = Math.max(p1, Math.max(p2, p3));
		return new Info(maxSubBSTSize, allSize, min, max);
	}

复杂度分析

  • 与二叉树的后续遍历复杂度一致,均是对每个节点访问了三次,O(n)
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值