判断一颗二叉树是否是完全二叉树

判断一颗二叉树是否是完全二叉树

方法一,采取标记的方法

非完全二叉树的三种情况

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

思路分析:

通过层序遍历来遍历树中的每一个非空结点

  • 遍历到的每一个结点都分为四种情况
    • 1.既有左孩子也有右孩子
      • 操作:将左孩子和右孩子入队列
    • 2.只有左孩子
      • 操作:
        • 1.首先判断标记是否处于激活状态,如果标记处于激活状态,直接返回 false
        • 2.满足标记条件,将标记设置为激活状态
    • 3.只有右孩子
      • 操作:直接返回 false,如果没有左结点而没有右结点,肯定不是一颗完全二叉树
    • 4.没有孩子结点
      • 操作:激活标记

实现代码

    /**
     * 判断一棵二叉树是否是完全二叉树
     *
     * @return 如果是返回 true,否则返回 false
     */
    public boolean isCompleteTree() {
        if (root == null)
            return true;
        return isCompleteTree(root);
    }

    /**
     * 通过层序遍历来遍历树中的每一个非空结点
     * <p>
     * 每一个结点都分为四种情况
     * 1.既有左孩子也有右孩子
     *  操作:将左孩子和右孩子入队列
     * 2.只有左孩子
     *  操作:
     *      1.首先判断标记是否处于激活状态,如果标记处于激活状态,直接返回 false
     *      2.满足标记条件,将标记设置为激活状态
     * 3.只有右孩子
     *  操作:直接返回 false,如果没有左结点而没有右结点,肯定不是一颗完全二叉树
     * 4.没有孩子结点
     *  操作:激活标记
     * <p>
     * 判断一棵二叉树是否是完全二叉树的实现
     *
     * @param root 根节点
     * @return 如果是返回 true,否则返回 false
     */
    private boolean isCompleteTree(Node<T> root) {
        // 申请一个队列
        ArrayDeque<Node<T>> queue = new ArrayDeque<>();
        // 将根节点添加进去
        queue.add(root);
        // 定义一个临时结点,存储的值为队列的队头结点
        Node<T> head;
        // 定义一个标记,标记当前访问的结点出度是否为 2 ,如果结点只有左节点而没有右结点 flag = true
        boolean flag = false;
        while (!queue.isEmpty()) {
            // 执行出队列操作
            head = queue.pop();
            // 如果结点的左子树为空但右子树不为空,此时不满足完全二叉树的条件,直接返回 false
            if (head.left == null && head.right != null)
                return false;
            // 如果标志为 true,并且当前结点的左孩子结点不为空
            if (flag && head.left != null)
                return false;
            // 如果当前结点的出度不为 2
            if (head.left == null || head.right == null)
                flag = true;
            // 当前结点的左孩子不为空
            if (head.left != null)
                queue.add(head.left);
            // 当前结点的右孩子不为空
            if (head.right != null)
                queue.add(head.right);
        }
        return true;
    }
  • 方法二,值为 null 的结点标记法

一颗完全二叉树

在这里插入图片描述

一颗非完全二叉树

在这里插入图片描述

思路分析:

  • 通过层序遍历来遍历树中的每一个结点
  • 采取特殊结点值为 null 替代 null 结点,入队列的标记方法
  • 每一个结点分为两种情况
  1. 如果当前结点不为空,直接将当前结点入队列
  2. 如果当前结点为空,将结点值为 null 的标记结点入队列
  • 当出队列的结点的值为标记结点 null 时,终止循环
  • 接着循环队列,条件为队列不为空,进行出队列操作,
  • 当出现队列出的结点的值不为 null 时,返回 false
  • 循环结束,返回 true,说明是一个完全二叉树

实现代码

    /**
     * 判断一棵树是否是一个完全二叉树
     *
     * @return 如果是返回 true,否则返回 false
     */
    public boolean isCompleteTree2() {
        if (root == null)
            return true;
        return isCompleteTree2(root);
    }

    /**
     * 通过层序遍历来遍历树中的每一个结点
     * <p>
     * 采取特殊结点值为 null 替代 null 结点,入队列的标记方法
     * <p>
     * 每一个结点分为两种情况
     *  1.如果当前结点不为空,直接将当前结点入队列
     *  2.如果当前结点为空,将结点值为 null 的结点入队列
     * <p>
     * 当出队列的结点的值为标记结点 null 时,终止循环
     * <p>
     * 接着循环队列,条件为队列不为空,进行出队列操作,
     * 当出现队列出的结点的值不为 null 时,返回 false
     * 循环结束,返回 true,说明是一个完全二叉树
     * <p>
     * <p>
     * 判断一棵二叉树树是否是一颗完全二叉树的实现方法
     *
     * @param root 根节点
     * @return 如果是返回 true,否则返回 false
     */
    private boolean isCompleteTree2(Node<T> root) {
        // 定义一个泛型为 Node 类型的队列
        ArrayDeque<Node<T>> queue = new ArrayDeque<>();
        // 将根节点入队列
        queue.offer(root);
        // 定义一个结点,值为队列的头一个
        Node<T> node = queue.peek();
        // 当结点的值为 标记节点 null 的时候,直接退出循环
        while (null != node.value) {
            queue.pop();
            // 如果结点的左孩子不为 null,将左孩子入队列
            // 否则插入一个值为 null 的 Node 对象
            queue.offer(node.left != null ? node.left : new Node<>(null));
            // 如果结点的左孩子不为 null,将左孩子入队列
            queue.offer(node.right != null ? node.right : new Node<>(null));
            node = queue.peek();
        }
        while (!queue.isEmpty())
            if (queue.pop().value != null)
                return false;
        return true;
    }
  • 11
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值