L3-010 是否完全二叉搜索树 (30 分)【巧用map存二叉树】

题目链接:L3-010 是否完全二叉搜索树 (30 分)

题目内容:

将一系列给定数字顺序插入一个初始为空的二叉搜索树(定义为左子树键值大,右子树键值小),你需要判断最后的树是否一棵完全二叉树,并且给出其层序遍历的结果。

输入格式:

输入第一行给出一个不超过20的正整数N;第二行给出N个互不相同的正整数,其间以空格分隔。

输出格式:

将输入的N个正整数顺序插入一个初始为空的二叉搜索树。在第一行中输出结果树的层序遍历结果,数字间以1个空格分隔,行的首尾不得有多余空格。第二行输出YES,如果该树是完全二叉树;否则输出NO。

输入样例1:

9
38 45 42 24 58 30 67 12 51

输出样例1:

38 45 24 58 42 30 12 67 51
YES

输入样例2:

8
38 24 12 45 58 67 42 51

输出样例2:

38 45 24 58 42 12 67 51
NO

先上代码,看完代码后我们讲解:

#include<bits/stdc++.h>
using namespace std;
int n;
map<int,int> t;   //map存树
void work(int a,int va){  //递归自上而下建树
    if(t.count(a)==0) {t[a] = va; return ;}
    if(t[a]<va) work(2*a, va);
    else work(2*a+1,va);
}
int main(){
    cin>>n;
    for(int i=0;i<n;i++){int va; cin>>va; work(1, va);}
    int flag=1; //标志:判断是否为完全二叉树。
    cout<<t[1];
    for(auto i:t){
        if(i.first==1) continue;
        if(flag&&i.first!=flag+1) flag=0; //若是不连续那么标志设为0,则不为完全二叉树
        else if(flag&&i.first==flag+1) flag++; 
        cout<<" "<<i.second;
    }
    return printf("\n%s\n", (flag?"YES":"NO"))&0;
}

虽然难度是L3,但是对于题目所涉及到的问题只需要你先建树,然后判断树是否为一棵完全二叉树,这并不难。

对于二叉搜索树是自上而下递归建树,建树就涉及到用什么来存树结点,一般是链式存储或者数组顺序存储。

用链式存储一般需要较多的代码量,但是占用很少的空间。
用数组顺序存储,需要开一个合适的数组大小,若是遇到了单链情况的二叉树,那么将会很浪费空间,甚至是爆int的空间,(但是数组可以越界访问只是很危险,这道题可以用数组来做,你甚至可以只给数组开一个空间,可能是由于数据的原因,亲测ac!具体原理自行百度QAQ

--------------------------------------------手动分割线----------------------------------------------

- [ QWQ] 好了,步入正题,我们接下来讨论的是用map来存树。

不了解map的自行科普,我这里简单提到我们将要用到的一点关于map的知识

  1. map的本质是红黑树,它的元素是pair类型,first是键,second是值。
  2. map对象是一个模板类,需要传两个参数,参数为键值对的数据的类型,我们用map存树其实就是用map模拟数组,自然是要用map<int,int> T;
  3. map可以用数组形式构造,比如 T[ key ] = value; 如果map中没有key那么就会自动创建一个key-value,若是存在,则会将原来的值更新为value。
  4. 判断map中是否存在key,用count()方法,比如 T.count(key),返回结果是bool类型。

基础知识具备了,我们来讲实现!

#include<map>
map<int,int> T;
void bt(int idx, int value){
    if(T.count(idx)==0){ //如果为空,那么就创建一个键值对
        T[idx] = value;
        return ;
    }
    if(value>T[idx]) bt(2*idx, value);  //如果大于就是左子树
    else if(value<T[idx]) bt(2*dx+1, value); //如果小于就是右子树
}

在函数体的时候,idx始终为根的位置,也就是一般来说是1,也可以是0,这样写函数时建左、右子树的idx分别为 2idx+1, 2idx+2。

接着讲遍历方式:
一般map有迭代器遍历,数组形式遍历,上面代码用的就是迭代器遍历,利用标志判断是否为完全二叉树,

下面写一下用数组形式遍历

for(int i=1;i<T.size();i++){
    if(T[i]){
        cout<<T[i];
    }
}

我们知道 i 的意义是结点标号,但是T.size()可能很小,但是 i 可能很大,那么为什么可以这样遍历呢?

就是因为 if(T [ i ] )

map可以用数组方式访问,也可以用数组方式构造,所以当map里面没有 键 i 的时候就是自动建立一个键值对,不赋值的话那么为默认值,int的默认值为0,string的默认值是空字符串。既然创建了,那么它的大小也是随之变化的,所以可以这样访问,但是这样一直都在申请空间,如果是单链二叉树的话,那么依旧会造成很大的空间浪费,所以我们可以用count()方法优化一下,但是这样 i 的条件就要不超过键的最大值。

下面我们优化一下:

int Tmax; //Tmax为结点编号的最大值
for(int i=1;i<=Tmax;i++){
    if(T.count(i)==1){
        cout<<T[i];
    }
}

Tmax如何获得呢,只需要在创建键值对的时候加一个条件即可,这个留给读者自己实现。

结束!over!

@跑龙套的花灵龙
@2021-03-29 15:31
@团队程序设计天梯赛-练习集
@完全二叉树
@二叉搜索树
@map

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值