当前位置: 首页 > news >正文

【PAT甲级】1123 Is It a Complete AVL Tree

✍个人博客:https://blog.csdn.net/Newin2020?spm=1011.2415.3001.5343
📚专栏地址:PAT题解集合
📝原题地址:题目详情 - 1123 Is It a Complete AVL Tree (pintia.cn)
🔑中文翻译:判断完全 AVL 树
📣专栏定位:为想考甲级PAT的小伙伴整理常考算法题解,祝大家都能取得满分!
❤️如果有收获的话,欢迎点赞👍收藏📁,您的支持就是我创作的最大动力💪

1123 Is It a Complete AVL Tree

An AVL tree is a self-balancing binary search tree. In an AVL tree, the heights of the two child subtrees of any node differ by at most one; if at any time they differ by more than one, rebalancing is done to restore this property. Figures 1-4 illustrate the rotation rules.
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

Now given a sequence of insertions, you are supposed to output the level-order traversal sequence of the resulting AVL tree, and to tell if it is a complete binary tree.

Input Specification:

Each input file contains one test case. For each case, the first line contains a positive integer N (≤ 20). Then N distinct integer keys are given in the next line. All the numbers in a line are separated by a space.

Output Specification:

For each test case, insert the keys one by one into an initially empty AVL tree. Then first print in a line the level-order traversal sequence of the resulting AVL tree. All the numbers in a line must be separated by a space, and there must be no extra space at the end of the line. Then in the next line, print YES if the tree is complete, or NO if not.

Sample Input 1:

5
88 70 61 63 65

Sample Output 1:

70 63 88 61 65
YES

Sample Input 2:

8
88 70 61 96 120 90 65 68

Sample Output 2:

88 65 96 61 70 90 120 68
NO

题意

AVL 树是一种自平衡二叉搜索树。

AVL 树中,任何节点的两个子树的高度最多相差 1 个。

如果某个时间,某节点的两个子树之间的高度差超过 1 ,则将通过树旋转进行重新平衡以恢复此属性。

现在,给定插入序列,请你输出得到的 AVL 树的层序遍历,并判断它是否是完全二叉树。

思路

这道题其实就是对平衡二叉树的构建和判断是否为完全二叉树的结合,我们分两部分进行讲解。

首先,平衡二叉树失衡主要分为四种情况,下面讨论的是导致失衡前最后插入的一个结点位置:

  1. 插入的结点在失衡结点的左孩子的左子树,直接右旋失衡结点。
  2. 插入的结点在失衡结点的左孩子的右子树,先对失衡结点的左孩子进行左旋,再对失衡结点右旋
  3. 插入的结点在失衡结点的右孩子的右子树,直接左旋失衡结点。
  4. 插入的结点在失衡结点的右孩子的左子树,先对失衡结点的右孩子进行右旋,再对失衡结点左旋

其次,再来看看该如何判断是否为完全二叉树:

  1. 我们可以利用完全二叉树的性质,用一个一维数组来存储,如果当前结点第下标为 k (假设下标从 1 开始),则满足以下条件:
    • 该结点的左孩子下标为 k * 2
    • 该结点的右孩子下标为 k * 2 + 1
  2. 所以如果一个二叉树是完全二叉树的话,将它所有结点按上述方式存储到一维数组中是可以刚好填满 1 ∼ N 1\sim N 1N(下标从 1 1 1 开始)个位置的。相反如果不是完全二叉树,则 1 ∼ N 1\sim N 1N 个位置中会有空余,也就是说最后一个结点的位置会大于 N N N 。故我们只用判断最后一个结点的位置是否为 N N N 即可,如果是 N N N 则说明是完全二叉树。
  3. 最后输出判断结果,注意如果是完全二叉树还需要输出最后一个结点的编号,否则输出该树的根结点编号。

我们拿题目的第一个样例举例,可以得到下面这颗完全二叉树:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WcS0WDA8-1664757434270)(PAT 甲级辅导.assets/7.png)]

其在一维数组中的存储情况为:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SYmu22fO-1664757434273)(PAT 甲级辅导.assets/8.png)]

代码

#include<bits/stdc++.h>
using namespace std;

const int N = 25;
int n;
int l[N], r[N], h[N], v[N], idx;
int q[N], pos[N];

//更新当前结点的深度
void update(int u)
{
    h[u] = max(h[l[u]], h[r[u]]) + 1;
}

//右旋
void R(int& u)
{
    int p = l[u];
    l[u] = r[p], r[p] = u;
    update(u), update(p);
    u = p;
}

//左旋
void L(int& u)
{
    int p = r[u];
    r[u] = l[p], l[p] = u;
    update(u), update(p);
    u = p;
}

//获取左右结点深度之差
int get_balance(int u)
{
    return h[l[u]] - h[r[u]];
}

//插入结点
void insert(int& u, int w)
{
    if (!u)  u = ++idx, v[u] = w;
    else if (w < v[u])
    {
        insert(l[u], w);
        if (get_balance(u) == 2)
        {
            //如果插入结点在失衡结点的左孩子的左子树,则对失衡结点右旋
            if (get_balance(l[u]) == 1)   R(u);
            //如果插入结点在失衡结点的左孩子的右子树,则先对左孩子左旋,再对失衡结点右旋
            else    L(l[u]), R(u);
        }
    }
    else
    {
        insert(r[u], w);
        if (get_balance(u) == -2)
        {
            //如果插入结点在失衡结点的右孩子的右子树,则对失衡结点左旋
            if (get_balance(r[u]) == -1)   L(u);
            //如果插入结点在失衡结点的右孩子的左子树,则先对右孩子右旋,再对失衡结点左旋
            else    R(r[u]), L(u);
        }
    }

    update(u);	//更新当前结点的深度
}

//判断是否是完全二叉树
bool bfs(int root)
{
    int hh = 0, tt = 0;
    q[0] = root;
    pos[root] = 1;

    bool res = true;
    while (hh <= tt)
    {
        int t = q[hh++];

        if (pos[t] > n)    res = false;

        if (l[t])   q[++tt] = l[t], pos[l[t]] = pos[t] * 2;
        if (r[t])   q[++tt] = r[t], pos[r[t]] = pos[t] * 2 + 1;
    }

    return res;
}

int main()
{
    cin >> n;

    int root = 0;
    for (int i = 0; i < n; i++)
    {
        int w;
        cin >> w;
        insert(root, w);
    }

    bool res = bfs(root);

    cout << v[q[0]];
    for (int i = 1; i < n; i++)    cout << " " << v[q[i]];
    cout << endl;

    if (res)    puts("YES");
    else    puts("NO");

    return 0;
}

相关文章:

  • PWM实验(控制蜂鸣器,风扇,马达)
  • MySQL 从入门到入狱 rm - rf /* 咳咳~ 到精通
  • 回溯算法 - 二叉树中和为某一值的路径 字符串的排列
  • 纯C实现的贪吃蛇(无EasyX,详解)
  • JAVA计算机毕业设计SUNHome家政服务管理平台Mybatis+系统+数据库+调试部署
  • 【项目管理】Java离线版语音识别-语音转文字
  • HTML5标签+基础特性
  • git的相关操作
  • ES6--》读懂JS中—Class类
  • 机器学习笔记(三)
  • 【Java 面试题】经典 Java 面试题 200 问(下)
  • 瑞吉外卖之 redis优化缓存
  • [JavaWeb]—前端篇
  • 机器学习感知机原理及python代码实现
  • js 对象
  • 【跃迁之路】【699天】程序员高效学习方法论探索系列(实验阶段456-2019.1.19)...
  • CSS实用技巧
  • HashMap剖析之内部结构
  • HTTP中的ETag在移动客户端的应用
  • JavaScript/HTML5图表开发工具JavaScript Charts v3.19.6发布【附下载】
  • leetcode98. Validate Binary Search Tree
  • LeetCode算法系列_0891_子序列宽度之和
  • Mithril.js 入门介绍
  • React 快速上手 - 06 容器组件、展示组件、操作组件
  • Redis字符串类型内部编码剖析
  • Tornado学习笔记(1)
  • Vue学习第二天
  • 搭建gitbook 和 访问权限认证
  • 干货 | 以太坊Mist负责人教你建立无服务器应用
  • 关于 Cirru Editor 存储格式
  • 名企6年Java程序员的工作总结,写给在迷茫中的你!
  • 让你的分享飞起来——极光推出社会化分享组件
  • 实现简单的正则表达式引擎
  • 使用iElevator.js模拟segmentfault的文章标题导航
  • 双管齐下,VMware的容器新战略
  • 移动互联网+智能运营体系搭建=你家有金矿啊!
  • 在Docker Swarm上部署Apache Storm:第1部分
  • 在weex里面使用chart图表
  • 翻译 | The Principles of OOD 面向对象设计原则
  • 国内唯一,阿里云入选全球区块链云服务报告,领先AWS、Google ...
  • ​MySQL主从复制一致性检测
  • # 透过事物看本质的能力怎么培养?
  • #define、const、typedef的差别
  • (2)STL算法之元素计数
  • (4)Elastix图像配准:3D图像
  • (Matlab)使用竞争神经网络实现数据聚类
  • (Oracle)SQL优化技巧(一):分页查询
  • (附源码)计算机毕业设计ssm电影分享网站
  • (理论篇)httpmoudle和httphandler一览
  • (三)uboot源码分析
  • (三)终结任务
  • (一)使用IDEA创建Maven项目和Maven使用入门(配图详解)
  • (转)【Hibernate总结系列】使用举例
  • (转)chrome浏览器收藏夹(书签)的导出与导入
  • (转)memcache、redis缓存