如何找到最大的子树是搜索二叉树

286 阅读4分钟

前言

本文主要介绍如何在一棵二叉树中找到子树是搜索二叉树,并且是最大的那棵子树。

正文

先来回顾下什么是搜索二叉树,搜索二叉树具有以下特性:

  • 若他的左子树不为空,则左子树上所有节点的值都小于根节点的值。
  • 若他的右子树不为空,则右子树上所有节点的值都大于根节点的值。
  • 他的左右子树也分别为二叉搜索树。

那么在一棵二叉树中,可能这个二叉树整体不是搜素二叉树,但这个二叉树的某一个子树可能是搜索二叉树,那么怎么才能找到这个子树呢?

以下图为例,下面这棵二叉树整体不是搜索二叉树,但是以3为节点的子树是搜索二叉树,以7位节点的子树也是搜索二叉树,并且以3为节点的子树一共有4个节点而以7位节点的子树共有3个节点,所以我们的目的是要把以3为节点的子树给找出来。

思路分析

那么如何找到一个二叉树的最大搜索子树呢?我们可以从以下几个方面考虑:

  1. 假如以节点X为根节点的二叉树整体不是搜索二叉树,需要判断:
  • 左树是否为搜索二叉树、左树的节点个数
  • 右树是否为搜索二叉树、右树的节点个数
  1. 假如以节点X为根节点的二叉树整体是搜索二叉树,需要判断:
  • 左树整体是否是搜索二叉树
  • 右树整体是否是搜索二叉树
  • 左树中最大节点的值要小于X
  • 右树中最小节点的值要大于X
  • 左树节点个数+右树节点个数+1为整体的节点个数

按照递归的套路来讲,在递归时需要一些信息,根据上面的分析可以得出需要的信息为:

  1. 最大子树搜索二叉树的节点数
  2. 是否为搜索二叉树
  3. 子树节点最大值
  4. 子树节点最小值
  5. 子树节点个数

其中第2个信息是否为搜索二叉树可以省略调,可以通过最大子树搜索二叉树的节点和子树节点个数是否相等来判断。

代码实现

先定义一个数的节点,代码如下:

public class TreeNode {
   public int val;
   public TreeNode left;
   public TreeNode right;
}

定义下递归返回所需要的信息:

public class Info {
	 // 最大子树搜索二叉树的节点数
   public int maxSubBSTSize;
   // 子树节点个数
   public int size;
   // 子树节点最大值
   public int max;
   // 子树节点最小值
   public int min;

   public Info(int maxBSTSubtreeSize, int size, int max, int min) {
      this.maxBSTSubtreeSize = maxBSTSubtreeSize;
      this.size = size;
      this.max = max;
      this.min = min;
   }
}

递归函数的实现:

public Info process(TreeNode x) {
   if (x == null) {
      return null;
   }
   // 收集左树信息
   Info leftInfo = process(x.left);
   // 收集右树信息
   Info rightInfo = process(x.right);
   // 整理本节点信息
   int max = x.val;
   int min = x.val;
   int size = 1;
   if (leftInfo != null) {
      max = Math.max(leftInfo.max, max);
      min = Math.min(leftInfo.min, min);
      size += leftInfo.size;
   }
   if (rightInfo != null) {
      max = Math.max(rightInfo.max, max);
      min = Math.min(rightInfo.min, min);
      size += rightInfo.size;
   }
   int p1 = -1;
   if (leftInfo != null) {
      p1 = leftInfo.maxSubBSTSize;
   }
   int p2 = -1;
   if (rightInfo != null) {
      p2 = rightInfo.maxSubBSTSize;
   }
   int p3 = -1;
   boolean leftBST = leftInfo == null ? true : (leftInfo.maxSubBSTSize == leftInfo.size);
   boolean rightBST = rightInfo == null ? true : (rightInfo.maxSubBSTSize == rightInfo.size);
   if (leftBST && rightBST) {
      boolean leftMaxLessX = leftInfo == null ? true : (leftInfo.max < x.val);
      boolean rightMinMoreX = rightInfo == null ? true : (x.val < rightInfo.min);
      if (leftMaxLessX && rightMinMoreX) {
         int leftSize = leftInfo == null ? 0 : leftInfo.size;
         int rightSize = rightInfo == null ? 0 : rightInfo.size;
         p3 = leftSize + rightSize + 1;
      }
   }
   return new Info(Math.max(p1, Math.max(p2, p3)), size, max, min);
}

在计算maxSubBSTSize时,根据思路分析里,需要对节点X进行分情况考虑:当节点X所在子树是搜索二叉树以及当节点X所在子树不是搜索二叉树着两种情况进行分析。

如果当节点X所在子树不是搜索二叉树,这种情况简单一些,只需要判断左右子树的最大节点值即可。

如果当节点X所在子树是搜索二叉树,需要判断左右子树是否为搜索二叉树以及左右子树的最大值、最小值要符合搜索二叉树的定义。

最后,主函数调用,代码如下:

public int largestBSTSubtree(TreeNode head) {
   if (head == null) {
      return 0;
   }
   return process(head).maxSubBSTSize;
}

总结

本文主要介绍如何在一棵二叉树中找到子树是搜索二叉树,在实现过程中进行了多种情况的分析,最终使用递归的套路进行了代码实现,本次分析时情况比较多,也容易漏掉某个点,如果有不清晰的地方,欢迎留言讨论~