二叉树的递归套路——完全二叉树

简介: 二叉树的递归套路——完全二叉树


给定一棵二叉树的头节点head,返回这颗二叉树中是不是完全二叉树

什么是完全二叉树,一句话可以总结——这棵树的每一层,要么就是满的,要么就是从左到右依次变满的。

方法一

(网上最常见的,不用二叉树的递归套路):宽度优先遍历这棵树,如有不了解宽度优先遍历的,可以看看这篇文章——二叉树的按层遍历。在宽度优先遍历二叉树的时候,做如下判断:

  • 遇到的每一个结点,如果有右孩子,但是没有左孩子,一定不是完全二叉树。因为一定是要从左到右依次变满的。
  • 一旦遇到第一个左右孩子不双全的结点,接下来遇到的所有结点,都必须是叶子结点。 这种情况下,一定是完全二叉树。

给大伙画棵树,大家自己对着上面两个条件判断一下。

image.png

方法二

二叉树的递归套路,二叉树的递归套路威力是无穷的,关键在于列可能性。判断一棵树是否为完全二叉树,我们以最后一层的最后一个结点到哪了进行分类。

1)无缺口:所有层都是满的,没有缺口位置(缺口就是最后一层成长到的位置。)这种情况下这棵树就是满二叉树。

此时,我们需要向左树要的信息是:(假设以X为头节点,下文也是)左树整体是否是满二叉树+左树的高度。 右树也一样。如果左右都是满二叉树的并且高度一样,那么以X为头节点的整颗树就是满二叉树。

2)有缺口:又有三种可能

1》:缺口停留在左树的位置,没有越过左树边界到右树上去。

满足这种情况需要的条件是:

左树整体是完全二叉树
&&
右树整体是满二叉树
&&
左树高度==右树高度+1

2》:左树成长情况是左树已经撑满了,右树全为空。

左树是满二叉树 && 右树是满二叉树 && 左树高度==右树高度+1

3》:最后一层成长的位置把左树撑满了,并且来到了右树上。

左树是满二叉树 && 右树是完全二叉树 && 左右高度一样

以上将所有可能性全部列了出来,如果四种情况都不成立,则必定不是完全二叉树,如果四个中有一个成立就是完全二叉树。

接下来进行整合,向每颗子树要的信息就是如下三个:

  1. 整颗子树是否是满二叉树
  2. 整颗子树是否是完全二叉树
  3. 整颗子树的高度

完整代码:

package com.harrison.class08;
import java.util.LinkedList;
import java.util.Queue;
public class Code08_IsCBT {
  public static class Node {
    public int value;
    public Node left;
    public Node right;
    public Node(int data) {
      this.value = data;
    }
  }
  public static boolean isCBT1(Node head) {
    if(head==null) {
      return true;
    }
    Queue<Node> queue=new LinkedList<>();
    queue.add(head);
    Node L=null;
    Node R=null;
    // 是否遇到左右两个孩子不双全的结点,一开始默认没有遇到
    boolean leaf=false;
    while(!queue.isEmpty()) {
      head=queue.poll();
      L=head.left;
      R=head.right;
      // 条件1:如果一旦遇到某个结点有右孩子但是没右左孩子(有右无左),必定不是完全二叉树
      // 条件2:如果遇到第一个左右孩子不双全的结点,接下来遇到的所有结点都必须是叶子节点,
      // 否则必定不是完全二叉树
      if(
        (L==null && R!=null)
        ||
        (leaf && (L!=null || R!=null))
        ) {
        return false;
      }
      // 接下来开始玩宽度优先遍历
      if(L!=null) {
        queue.add(L);
      }
      if(R!=null) {
        queue.add(R);
      }
      // 左右孩子有任何一个缺失,就说这个结点不双全,所以设置为true
      // 可能多次改为true,但不要紧,只要第一次从false改为true,之后永远都为true
      // 该判断只是为了说明:只要有两个孩子不双全的情况,这个遍历就要设置为true,
      // 其实只需要使用第一次设置为true的时候
      if(L==null || R==null) {
        leaf=true;
      }
    }
    return true;
  }
  public static class Info{
    public boolean isFull;
    public boolean isCBT;
    public int height;
    public Info(boolean full,boolean cbt,int h) {
      isFull=full;
      isCBT=cbt;
      height=h;
    }
  }
  public static Info process1(Node head) {
    if(head==null) {
      return new Info(true, true, 0);
    }
    Info leftInfo=process1(head.left);
    Info rightInfo=process1(head.right);
    int height=Math.max(leftInfo.height, rightInfo.height)+1;
    boolean isFull=leftInfo.isFull
               &&
               rightInfo.isFull
               &&
               leftInfo.height==rightInfo.height;
    boolean isCBT=false;
    if(isFull) {
      // 1)无缺口:所有层都是满的,
      // 没有缺口位置(缺口就是最后一层成长到的位置。)这种情况下这棵树就是满二叉树。
      isCBT=true;
    }else {
      // 分别是情况2)的 1》、2》3》
      if(leftInfo.isCBT && rightInfo.isCBT) {
        if(leftInfo.isCBT && rightInfo.isFull && leftInfo.height==rightInfo.height+1) {
          isCBT=true;
        }
        if(leftInfo.isFull && rightInfo.isFull && leftInfo.height==rightInfo.height+1) {
          isCBT=true;
        }
        if(leftInfo.isFull && rightInfo.isCBT && leftInfo.height==rightInfo.height) {
          isCBT=true;
        }
      }
    }
    return new Info(isFull, isCBT, height);
  }
  public static boolean isCBT2(Node head) {
    return process1(head).isCBT;
  }
  public static Node generateRandomBST(int maxLevel,int maxValue) {
    return generate(1, maxLevel, maxValue);
  }
  public static Node generate(int level,int maxLevel,int maxValue) {
    if(level>maxLevel || Math.random()<0.5) {
      return null;
    }
    Node head=new Node((int)(Math.random()*maxValue));
    head.left=generate(level+1, maxLevel, maxValue);
    head.right=generate(level+1, maxLevel, maxValue);
    return head;
  }
  public static void main(String[] args) {
    int testTimes=1000000;
    int maxLevel=5;
    int maxValue=100;
    for(int i=0; i<testTimes; i++) {
      Node head=generateRandomBST(maxLevel, maxValue);
      if(isCBT1(head)!=isCBT2(head)) {
        System.out.println("oops");
      }
    }
    System.out.println("finish");
  }
}

   


相关文章
|
1月前
|
存储 算法
数据结构与算法学习十六:树的知识、二叉树、二叉树的遍历(前序、中序、后序、层次)、二叉树的查找(前序、中序、后序、层次)、二叉树的删除
这篇文章主要介绍了树和二叉树的基础知识,包括树的存储方式、二叉树的定义、遍历方法(前序、中序、后序、层次遍历),以及二叉树的查找和删除操作。
25 0
|
5月前
|
存储 算法 编译器
技术经验解读:二叉树的遍历:先序中序后序遍历的递归与非递归实现及层序遍历
技术经验解读:二叉树的遍历:先序中序后序遍历的递归与非递归实现及层序遍历
38 0
|
6月前
|
Java BI 数据库管理
二叉树---前,中,后序遍历做题技巧(前,中,后,层次,线索二叉树)
二叉树---前,中,后序遍历做题技巧(前,中,后,层次,线索二叉树)
87 11
|
算法
代码随想录 Day11 二叉树 LeetCode T144,145,94 前中后序遍历 (递归解法)
代码随想录 Day11 二叉树 LeetCode T144,145,94 前中后序遍历 (递归解法)
47 0
|
6月前
|
算法
递归算法:二叉树前序、中序、后序遍历解析与递归思想深度剖析
递归算法:二叉树前序、中序、后序遍历解析与递归思想深度剖析
97 0
|
存储
二叉树相关问题细谈递归(上)
二叉树相关问题细谈递归
70 0
二叉树相关问题细谈递归(下)
二叉树相关问题细谈递归(下)
63 0
|
存储 算法 Java
二叉树及二叉树遍历的基础解读
二叉树(Binary tree)是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要。二叉树特点是每个结点最多只能有两棵子树,且有左右之分。 二叉树是n个有限元素的集合,该集合或者为空、或者由一个称为根(root)的元素及两个不相交的、被分别称为左子树和右子树的二叉树组成,是有序树。当集合为空时,称该二叉树为空二叉树。在二叉树中,一个元素也称作一个结点 。
155 0
二叉树及二叉树遍历的基础解读
【LeetCode剑指offer26】树的子结构(递归)
题目判断的是B是否为A树的【子结构】,而不判断是【子树】。 直观的思路: 从A的每个节点开始逐个(递
131 0
【LeetCode剑指offer26】树的子结构(递归)