上了半个学期的课以后才发现,原来从来没有认真听过的数算实习才是幕后的大BOSS…每次作业的题目都虐到爆,每节课讲的数据结构都是数算课程上面简单数据结构的升级版,第一次靠自己的能力强行AC了Treap,把自己都感动了。
简单总结一下树堆的性质:
1、树堆,顾名思义就是树和堆的结合体,每个结点除了value值以外还增加了一个priority的值,是每个结点的权值,以最大堆为例,权值最大的点始终在堆的顶部,同时又满足搜索二叉树的性质。
2、在插入一个新的节点的时候,先按照搜索二叉树的性质把插入到叶子节点的位置,然后再使用堆的性质把这个节点siftup到对应的位置,这个时候的问题就在于siftup的时候不能单纯的进行交换,而是采用一种叫做旋转的操作,如图所示:
这里以右旋为例,如果X的权值比Y的权值大,就把X和Y的位置互换,然后理所应当地把B接在Y的左孩子结点上(因为B的值比X大比Y小),我在这道题目中的代码实现比较繁琐,不知道有没有更好的方案,我觉得直接模拟实现就好,也需要什么优化。
3、每个结点的权值一般是直接用随机数生成的,这样也导致我每次提交所用的时间都是不一样的= -
4、当我完成树堆的构造之后,输出第i大的数的时候采用的是中序遍历的方法,结果依然TLE(特别想掀桌子),在室友的启发之下,采用了另一种找第k大的数的方法:给每个结点增加一个变量,记录以这个节点为根节点的子树的节点个数,在寻找第k大的数的时候相当于一个在二叉树里面检索某个值的方式来实现,在debug一个多小时之后顺利AC…
#include<iostream>
#include<limits.h>
#include<memory.h>
#include<stdlib.h>
#include<cstdio>
using namespace std;
#define MAXM 30050
struct node{
int value;
int size;
int priority;
int fa,lc,rc;
};
bool operator < (node a, node b){
return a.value < b.value;
}
int M,N;
int real_i = 0;
node treap[MAXM];
int copy_i = 0;
void Find2(int root,int k){
if(!root)
return;
if(treap[treap[root].lc].size +1 == k){
printf("%d\n",treap[root].value);
return;
}
if(treap[treap[root].lc].size >= k){
Find2(treap[root].lc,k);
}
else{
Find2(treap[root].rc,k-treap[treap[root].lc].size-1);
}
}
void insert(int _i,int root){
treap[root].size++;
treap[0].size = 0;
if(treap[_i].value > treap[root].value){
if(treap[root].rc){
insert(_i,treap[root].rc);
}
else{
treap[root].rc = _i;
treap[_i].fa = root;
}
}
else{
if(treap[root].lc){
insert(_i,treap[root].lc);
}
else{
treap[root].lc = _i;
treap[_i].fa = root;
}
}
}
void Rrot(int i,int j){
int k = treap[j].fa;
treap[i].fa = treap[j].fa;
if(treap[k].lc == j)
treap[k].lc = i;
else if(treap[k].rc == j)
treap[k].rc = i;
treap[j].lc = treap[i].rc;
treap[treap[j].lc].fa = j;
treap[i].rc = j;
treap[j].fa = i;
treap[j].size = treap[treap[j].lc].size + treap[treap[j].rc].size + 1;
treap[i].size = treap[treap[i].lc].size + treap[treap[i].rc].size + 1;
}
void Lrot(int i,int j){
int k = treap[j].fa;
treap[i].fa = treap[j].fa;
if(treap[k].lc == j)
treap[k].lc = i;
else if(treap[k].rc == j)
treap[k].rc = i;
treap[j].rc = treap[i].lc;
treap[treap[j].rc].fa = j;
treap[i].lc = j;
treap[j].fa = i;
treap[j].size = treap[treap[j].lc].size + treap[treap[j].rc].size + 1;
treap[i].size = treap[treap[i].lc].size + treap[treap[i].rc].size + 1;
}
void siftup(int i){
if(!i)
return;
int j = treap[i].fa;
if(treap[i].priority > treap[j].priority){
if(treap[j].lc == i){
Rrot(i,j);
siftup(i);
}
else if(treap[j].rc == i){
Lrot(i,j);
siftup(i);
}
}
return;
}
int main(){
memset(treap,0,sizeof(treap));
scanf("%d%d",&M,&N);
treap[0].priority = INT_MAX;
treap[0].value = INT_MIN;
treap[0].size = 0;
for(int i=1;i<=M;++i){
scanf("%d",&treap[i].value);
treap[i].priority = rand();
}
int mark;
int i = 1;
while(N--){
scanf("%d",&mark);
for(;i<=mark;++i){
treap[i].size = 1;
insert(i,treap[0].rc);
siftup(i);
}
real_i++;
Find2(treap[0].rc,real_i);
}
//system("pause");
return 0;
}