P5076 【深基16.例7】普通二叉树(简化版)

题目描述

您需要写一种数据结构,来维护一些数( 都是 10^9109 以内的数字)的集合,最开始时集合是空的。其中需要提供以下操作,操作次数 qq 不超过 10^4104:

  1. 查询 xx 数的排名(排名定义为比当前数小的数的个数 +1+1。若有多个相同的数,应输出最小的排名)。
  2. 查询排名为 xx 的数。
  3. 求 xx 的前驱(前驱定义为小于 xx,且最大的数)。若未找到则输出 -21474836472147483647。
  4. 求 xx 的后继(后继定义为大于 xx,且最小的数)。若未找到则输出 21474836472147483647。
  5. 插入一个数 xx。

输入格式

输出格式

输入输出样例

输入 #1
7
5 1
5 3
5 5
1 3
2 2
3 3
4 3
输出 #1
2
3
1
5

使用容器:std::multiset


mulitset是什么?

multiset是C++ STL里的一种容器。

头文件 #include<set>


multiset有什么性质?

  • 里面的元素按顺序排列,默认升序。
  • 不去重(这点和set是不同的)。

常用方法

multiset<int>q;
//定义一个multiset,尖括号里写类型
//如果是自定义类型,需要重载小于号 

q.insert(x);
//插入一个数 x 

q.clear();
//清空 

q.erase(x);
//删除容器中的所有值为 x 的数 

q.erase(it);
//删除容器中迭代器it指向的元素 

q.empty();
//返回bool值,如果容器为空返回true,否则返回false 

q.size()
//返回元素个数

q.begin();
//返回首个元素的迭代器 

q.end();
//返回最后一个元素的下一个位置的迭代器 

q.count(x);
//返回容器中 x 的个数 

q.find(x);
//返回容器中第一个x的位置(迭代器),如果没有就返回q.end() 

q.lower_bound(x);
//返回容器中第一个大于等于x的数的迭代器 

q.upper_bound(x);
//返回容器中第一个大于x的数的迭代器 

以上是常用的方法。

值得一提的是,lower_bound方法与upper_bound方法,都是内置二分查找,速度要比遍历暴力查找快很多。


分析题目

1. 查询 x 数的排名

排名,说白了就是排序之后的x的下标。

我们只要用lower_bound方法,找到第一个x的位置。

然后从begin开始往后遍历容器,只要达到这个位置,就输出当前下标即可。

2.查询排名为 x 的数

遍历容器,只要当前排名到达x,就输出当前值。

(因为multiset容器无法进行随机访问)

3.求 x 的前驱(前驱定义为小于 x,且最大的数)

前驱,也就是x的前一个。

我们只要用lower_bound方法找到第一个x的位置,然后输出上一个就OK了。

4.求 x 的后继(后继定义为大于 x,且最小的数)。

后继,也就是第一个大于x的数。

我们可以用upper_bound方法,直接找到这个值。

5.插入一个数 x

直接用insert方法插入即可。


坑点

题目描述其实是不全的,这一点我也是看讨论版才知道的。

  • 如果x没有前驱要输出-0x7fffffff

  • 如果x没有后继要输出 0x7fffffff

我们可以提前把这两个值放到容器里,那么,没有前驱的数的前驱一定是-0x7fffffff;没有后继的数的后继一定是0x7fffffff


代码

(详见注释)

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<set>
using namespace std;
multiset<int>q;
int n,t,x,order;
int main()
{
    q.insert(-0x7fffffff);
    q.insert(0x7fffffff);
    //提前放入这两个数,避免错误
    scanf("%d",&n);
    while(n--)
    {
        scanf("%d%d",&t,&x);
        if(t==1)
        {
            auto it=q.lower_bound(x);
            //auto是自动判断数据类型,只有C++14以上才支持
            //可以写作multiset<int>::iterator,因为lower_bound方法返回的是迭代器
            // it 取得 x 的位置
            
            order=0;
            //order为排名
            
            for(auto i=q.begin();i!=it;i++,order++);
            //这里的auto同理,也是迭代器
            //这里就处理出了x的排名——order
            
            printf("%d\n",order);
            //输出order即为答案
        }
        else if(t==2)
        {
            order=-1;
            //初值为-1是因为前面有一个-0x7fffffff,所以order要多跑一步

            for(int i:q)
                if(++order==x)
                //缩写,order先自增一,再判断是否与x相等
                //如果是(order++==x),那就是先判断再自增,这里要尤其注意
                    printf("%d\n",i);
                //i就是容器里的值,输出i

            //注意这里的for(:)循环,是只有C++11以上才支持的
            //和auto一样,都是noip不能用的
            //这种循环,i就是容器里的值而不是下标
            //也可以使用迭代器循环,上面的循环等价于
            /*
                for(multiset<int>::iterator it=q.begin();it!=q.end();it++)
                {
                    order++;
                    if(order==x)
                        printf("%d\n",*it);
                }
            */
            //这种循环是noip可以使用的
        }
        else if(t==3)
        {
            auto it=q.lower_bound(x);
            //取得第一个大于等于x的值
            //也就是第一个x的位置
            //由于我们要取得前驱,所以it要自减一
            printf("%d\n",*--it);
            //这句是先自减,再输出,是缩写
            //等价于:
            /*
                it--;
                printf("%d\n",*it);
            */
            //因为是迭代器(指针),所以输出前面加 *
        }
        else if(t==4)
        {
            printf("%d\n",*q.upper_bound(x));
            //要取得后继,就是第一个大于x的值
            //用upper_bound方法取得第一个大于x的迭代器
            //输出即可
            //因为是迭代器(指针),所以输出前面加 *
        }
        else
        {
            q.insert(x);
            //直接添加即可
        }
    }
    return 0;
}
posted @ 2022-05-04 17:07  LuoJMeng  阅读(107)  评论(0编辑  收藏  举报