數據結構入門1—Treap

debug3個月把洛谷刷屏,終於過了。。。。(一開始寫了兩百行,後來看了大孫代碼才搞點小技巧改短了些)

treap,樹堆,是指有一個隨機附加域滿足堆的性質的二叉搜索樹,其結構相當於以隨機數據插入的二叉搜索樹。其基本操作的期望時間複雜度爲O(logn)。相對於其他的平衡二叉搜索樹,Treap的特點是實現簡單,且能基本實現隨機平衡的結構。我們可以看到,如果一個二叉排序樹節點插入的順序是隨機的,這樣我們得到的二叉排序樹大多數情況下是平衡的,即使存在一些極端情況,但是這種情況發生的概率很小,所以我們可以這樣建立一顆二叉排序樹,而不必要像AVL那樣旋轉,可以證明隨機順序建立的二叉排序樹在期望高度是O(logn),但是某些時候我們並不能得知所有的帶插入節點,打亂以後再插入。所以我們需要一種規則來實現這種想法,並且不必要所有節點。也就是說節點是順序輸入的,我們實現這一點可以用Treap。Treap=Tree+Heap。

Treap是一棵二叉排序樹,它的左子樹和右子樹分別是一個Treap,和一般的二叉排序樹不同的是,Treap紀錄一個額外的數據,就是優先級。Treap在以關鍵碼構成二叉排序樹的同時,還滿足堆的性質(在這裏我們假設節點的優先級大於該節點的孩子的優先級)。但是這裏要注意的是Treap和二叉堆有一點不同,就是二叉堆必須是完全二叉樹,而Treap可以並不一定是。

以上來自百度百科。

簡而言之,treap中序遍歷就是原數組排好序後的排列,其中隨機出來的優先級滿足堆的性質。

與Splay不同之處是Splay的中序遍歷就是原數組的排列順序,而且與Splay的旋轉操作等不同。treap比Splay好寫(然而。。。)

新手很容易忽略的細節問題是一個數可能會添加多次,需要存一下每個數有多少個,每次查詢和修改不要忘了它。

Treap模板題(洛谷)

//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<ctime>
using namespace std;
const int maxn=100000+10;
int n,tot=0,ans1,ans2,root;

int aa,ff;char cc;
int read() {
	aa=0;ff=1;cc=getchar();
	while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
	if(cc=='-') ff=-1,cc=getchar();
	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
	return aa*ff;
}

struct Node{
	int num,rnd,son[2],sum,x;
}node[maxn];

void rotate(int &pos,int p) {
	int s=node[pos].son[p];
	node[s].sum=node[pos].sum;
	node[pos].son[p]=node[s].son[!p];
	node[s].son[!p]=pos;
	node[pos].sum=node[pos].x+node[node[pos].son[0]].sum+node[node[pos].son[1]].sum;
	pos=s;
}

void add(int &pos,int x) {
	if(!pos) {
		pos=++tot;node[pos].num=x;node[pos].rnd=rand();
		node[pos].sum=node[pos].x=1;
		return ;
	}
	node[pos].sum++;
	if(node[pos].num==x) {
		node[pos].x++; return;
	}
	int p=x>node[pos].num;
	add(node[pos].son[p],x);
	if(node[node[pos].son[p]].rnd<node[pos].rnd) rotate(pos,p);
}

void del(int &pos,int x) {
	if(!pos) return;
	if(node[pos].num==x) {
		if(node[pos].x>1) {
			node[pos].x--;node[pos].sum--;return;
		}
		if(node[pos].son[0]*node[pos].son[1]==0) {
			pos=node[pos].son[0]+node[pos].son[1]; return;
		}
		int p= node[node[pos].son[1]].rnd<node[node[pos].son[0]].rnd;
		rotate(pos,p);
		node[pos].sum--;
		del(node[pos].son[!p],x);
	}
	else {
		node[pos].sum--;
		if(node[pos].num>x) del(node[pos].son[0],x);
		else del(node[pos].son[1],x);
	}
}

int qrank(int pos,int x) {
	if(node[pos].num==x) return node[node[pos].son[0]].sum+1;
	if(node[pos].num>x) return qrank(node[pos].son[0],x);
	return node[node[pos].son[0]].sum+node[pos].x+qrank(node[pos].son[1],x);
}

int qnum(int pos,int x) {
	if(x>node[node[pos].son[0]].sum&&x<=node[node[pos].son[0]].sum+node[pos].x) return node[pos].num;
	if(x<=node[node[pos].son[0]].sum) return qnum(node[pos].son[0],x);
	return qnum(node[pos].son[1],x-node[pos].x-node[node[pos].son[0]].sum);
}

void q1(int pos,int x) {
	if(!pos) return;
	if(x>node[pos].num) {
		ans1=node[pos].num;
		q1(node[pos].son[1],x);
	}
	else q1(node[pos].son[0],x);
}

void q2(int pos,int x) {
	if(!pos) return;
	if(x<node[pos].num) {
		ans2=node[pos].num;
		q2(node[pos].son[0],x);
	}
	else q2(node[pos].son[1],x);
}

int main() {
	srand((unsigned)time(NULL));
	n=read();
	int opt,x,y;
	for(int i=1;i<=n;++i) {
		opt=read();x=read();
		if(opt==1) add(root,x);
		else if(opt==2) del(root,x);
		else if(opt==3) printf("%d\n",qrank(root,x));
		else if(opt==4) printf("%d\n",qnum(root,x));
		else if(opt==5) q1(root,x),printf("%d\n",ans1);
		else q2(root,x),printf("%d\n",ans2);
	}
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章