【可並堆】【數據結構】左偏樹簡介


左偏樹

Noip大概率翻皮水了,然后先繼續xjb學習吧,順便文化課也是翻皮水大隊的:(

今天介紹一種特殊的數據結構:可並堆中的一種->左偏樹(好吧其實是因為這種簡單易懂代碼復雜度較低).

基本介紹

左偏樹,故名思義,它是顆向左傾斜的樹,其實,它還是棵二叉樹,再者,它還具有堆的性質,but,它不是堆.

那么顯然,左偏樹看起來就像是優化堆一些難以用較優復雜度實現的操作,其實主要就是一個操作:合並.

我們知道,傳統的二叉堆,是需要暴力合並的,復雜度為\(O(sz1+sz2)\),而本文所涉及的左偏樹,復雜度為$O(log_{2} sz1sz2) $.

首先介紹一個定義:一個節點到其子樹內最近葉節點的距離稱之為這個節點的高度Height,簡記為ht.

接下來給出一些左偏樹的性質.

基本性質

性質1

堆的性質:對於任意節點P,\(val_{P}\)<(或>)\(val_{lson[p]}\)\(val_{rson[p]}\)

性質2

\[ht_{lson[P]} \geq ht_{rson[P]} \]

左偏樹顧名思義,向左傾斜的樹,就是這個性質在圖像可視化后的詮釋.

性質3

\[ht_{P}=ht_{lson[P]}+1 \]

很好理解,由性質2以及葉節點ht為0可以得出.

性質4

對於一棵\(n\)節點的左偏樹,\(max\{ht\} \leq log_{2}(n+1)-1\)

給出證明如下,設\(max\{ht\}=k\)

那么,顯然節點數最少的情況為一棵滿二叉樹,此時,節點數為\(2^{k+1}-1\)

\(n \geq 2^{k+1}-1\),由數學推導可得性質4.

這個性質主要保證了左偏樹的復雜度.

接下來簡單介紹一下一些基本操作.

基本操作

1.合並

這是左偏樹最基礎也是最重要的操作,這里介紹小根堆的情況:

首先,令根節點權值較小的為x,否則為y,記根節點分別為X、Y.

首先在根節點的右子樹最右鏈中找到第一個比Y大的位置,將Y作為其父親,然后不斷遞歸調用合並Y的右子樹和以該節點為根的右子樹即可,並維護更新相關信息.

然后發現更新后,右子樹的ht可能比左子樹大,此時交換兩個子樹即可.

其實上述合並過程的實現本質是一樣的,下面給出參考,對於復雜度的分析,不多給出解釋.

inline int merge(int x,int y){
	if (!x||!y) return x+y;
	if (v[x]>v[y]||(v[x]==v[y]&&x>y)) swap(x,y);
	ch[x][1]=merge(ch[x][1],y);
	f[ch[x][1]]=x;
	if (ht[ch[x][0]]<ht[ch[x][1]]) swap(ch[x][0],ch[x][1]);
	ht[x]=ht[ch[x][1]]+1;return x;
}

2.插入

本質上就是將一棵只有一個節點的左偏樹與當前樹合並.

3.查詢最大/最小值

直接返回根結點的權值.

4.刪除根節點

合並根節點兩棵子樹即可.

5.構造一棵新左偏樹

法1:

暴力插入

法2:

分治思想,分段處理,直至剩余1個節點,然后返回並merge,復雜度約為O(n).

6.刪除任意節點

子樹內維護與刪除根節點一樣,然后向上傳遞信息並更改,維護性質即可.

模板

給出模板題的模板

見下:

#include <stdio.h>
#define MN 100005
#define R register
inline int read(){
	R int x; R bool f; R char c;
	for (f=0; (c=getchar())<'0'||c>'9'; f=c=='-');
	for (x=c-'0'; (c=getchar())>='0'&&c<='9'; x=(x<<3)+(x<<1)+c-'0');
	return f?-x:x;
}
inline int max(int a,int b){return a>b?a:b;}
inline int min(int a,int b){return a<b?a:b;}
inline void swap(int &a,int &b){a^=b,b^=a,a^=b;}
int n,q,ch[MN][2],v[MN],ht[MN],f[MN];
inline int gf(int u){
	while(f[u]) u=f[u];
	return u;
}
inline int merge(int x,int y){
	if (!x||!y) return x+y;
	if (v[x]>v[y]||(v[x]==v[y]&&x>y)) swap(x,y);
	ch[x][1]=merge(ch[x][1],y);
	f[ch[x][1]]=x;
	if (ht[ch[x][0]]<ht[ch[x][1]]) swap(ch[x][0],ch[x][1]);
	ht[x]=ht[ch[x][1]]+1;return x;
}
inline void pop(int x){
	v[x]=-1;f[ch[x][0]]=f[ch[x][1]]=0;
	merge(ch[x][0],ch[x][1]);
}
int main(){
	n=read(),q=read();ht[0]=-1;
	for (R int i=1; i<=n; ++i) v[i]=read();
	for (R int i=1; i<=q; ++i)
		if (read()==1){
			R int x=read(),y=read();
			if (!(~v[x])||!(~v[y])) continue;
			x=gf(x),y=gf(y);if (x==y) continue;
			merge(x,y);
		}
		else{
			R int x=read();
			if (!(~v[x])) puts("-1");
			else {x=gf(x);printf("%d\n",v[x]);pop(x);}
		}
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM