平衡樹入門——Treap

平衡樹入門——Treap

挖坑一直爽,一直挖坑一直爽。這個坑大概鴿了一年。

1 平衡樹

平衡樹(Balance Tree,BT) 指的是,任意節點的子樹的高度差都小於等於1。常見的符合平衡樹的有,B樹(多路平衡搜索樹)、AVL樹(二叉平衡搜索樹)等。平衡樹可以完成集合的一系列操作, 時間複雜度和空間複雜度相對於“2-3樹”要低,在完成集合的一系列操作中始終保持平衡,爲大型數據庫的組織、索引提供了一條新的途徑。

\(Treap\) 是每個 OIer 入門必學平衡樹。

2 變量簡介

2.1 節點

struct node{
	int size,cnt;
	int val,rad;
	int l,r;
};
node tree[N];
int tail,root;

其中 \(size\) 是這顆子樹的大小,\(cnt\) 表示的是這個節點所代表權值的個數,\(val\) 表示的是節點代表權值,\(rad\) 是一個隨機數,隨機是因爲 Treap 爲了保持其平衡,同時維護堆的性質,這樣複雜度就有了保障。

2.2 合併信息

inline void pushup(int k){
	tree[k].size=tree[tree[k].l].size+tree[tree[k].r].size+tree[k].cnt;
}

比較顯然,不做講解。

2.3 左右旋

inline void zip(int &k){
	int kr=tree[k].r;
	tree[k].r=tree[kr].l;tree[kr].l=k;
	pushup(k);pushup(kr);k=kr;
}

inline void zap(int &k){
	int kl=tree[k].l;
	tree[k].l=tree[kl].r;tree[kl].r=k;
	pushup(k);pushup(kl);k=kl;
}

第一個函數是左旋,第二個是右旋。爲了維護這是爲了維護堆的性質,同時必須滿足 BST 的性質。

img

img

通過圖片不難理解上面的代碼。

2.4 建立新節點

inline int New(int val){
	tree[++tail].val=val;
	tree[tail].size=tree[tail].cnt=1;
	tree[tail].rad=rand();
	return tail;
}

2.5 插入與刪除

inline void insert_(int &k,int val){
	if(tail==0) k=0;
	if(!k) k=New(val);
	else if(val==tree[k].val) tree[k].cnt++;
	else if(val<tree[k].val){
		insert_(tree[k].l,val);
		if(tree[tree[k].l].rad>tree[k].rad) zap(k);
	}
	else{
		insert_(tree[k].r,val);
		if(tree[tree[k].r].rad>tree[k].rad) zip(k);
	}
	pushup(k);
}

插入實際上就是在爲這個權值找一個新的位置,與此同時維護一下堆的性質,最後合併就可以了。

inline void delete_(int &k,int val){
	if(!k) return;
	if(val==tree[k].val){
		if(tree[k].cnt>1){
			tree[k].cnt--;
		}
		else if(!tree[k].l&&!tree[k].r) k=0;
		else{
			if(!tree[k].r||tree[tree[k].l].rad>tree[tree[k].r].rad){
				zap(k);
				delete_(tree[k].r,val);
			}
			else{
				zip(k);
				delete_(tree[k].l,val);
			}
		}
	}
	else val<tree[k].val?delete_(tree[k].l,val):delete_(tree[k].r,val);
	pushup(k);
}

我們希望把刪除節點旋轉到葉子節點上進行刪除,當然進行這個操作得必須是這個點的 \(cnt\) 在刪除之前爲 \(1\)

。注意在旋轉的過程中,還是要小心維護其堆得性質,同時因爲我們的引用符號,我們直接在旋轉到葉子節點之後讓 \(k=0\) 就完成了刪除,非常方便。

2.6 查詢前驅後繼,按排名找值,按值找排名

inline int getrank(int k,int val){
	if(!k) return -INF;
	if(tree[k].val==val) return tree[tree[k].l].size+1;
	if(val<tree[k].val) return getrank(tree[k].l,val);
	return getrank(tree[k].r,val)+tree[tree[k].l].size+tree[k].cnt;
}

inline int getval(int k,int rank){
	if(!k) return -INF;
	if(tree[tree[k].l].size>=rank) return getval(tree[k].l,rank);
	if(tree[tree[k].l].size+tree[k].cnt>=rank) return tree[k].val;
	return getval(tree[k].r,rank-tree[tree[k].l].size-tree[k].cnt);
}

inline int getpre(int k,int val){
	if(!k) return -INF;
	if(tree[k].val>=val) return getpre(tree[k].l,val);
	else return Max(tree[k].val,getpre(tree[k].r,val));
}

inline int getnext(int k,int val){
	if(!k) return INF;
	if(tree[k].val<=val) return getnext(tree[k].r,val);
	else return Min(tree[k].val,getnext(tree[k].l,val));
}
	

這個好理解,不做講解。

例題

例題

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