模板:左偏樹
如果你知道priority_queue的話,那自然就知道左偏樹的目的了。
左偏樹的目的和優先隊列一致,就是求出當前所在堆中的最大(小)值。
但是我們作為高貴的C++選手,我們為什麽還要學習左偏樹呢。
當然是因為priority_queue太!慢!了!
————————————————————————————————————
概念引入:
對於左偏樹,我們引入兩個概念:
外節點:如果該節點的左子樹或右子樹為空,那麽該節點為外節點。
距離(dis):該節點到達最近的外節點經過的邊的個數。
我們同時將優先隊列的定義照搬過來:
鍵值(val):節點的權值。
同時將優先隊列的性質照搬過來:
性質1:節點的鍵值小於或等於它的左右子節點的鍵值。
對於左偏樹,賦予它一個性質:
性質2:節點的左子節點的距離不小於右子節點的距離。
同時可以推出:
性質3:節點的距離等於它的右子節點的距離加1。(顯然)
為了滿足性質3,我們規定空節點的dis為-1。
此外還有很多性質,不過都是跟推左偏樹復雜度相關的性質,有興趣可以百度。
——————————————————————
復雜度:
這裏直接給出左偏樹的復雜度:
合並:O(logN1+logN2)(N1和N2分別為待合並左偏樹的節點個數)
插入:O(logn)
刪除最小節點:O(logn)
建樹:O(n)
刪除已知編號節點:O(logn)
————————————————————
基本操作:
以建立大根堆為例,現將前三個操作說明。
合並:
按照左偏樹的定義合並即可。
設要合並的兩個堆的根節點為x,y。
如果其中一個堆為空堆,則不需合並。
否則比較它們的val,設x為val較小的堆根節點。
則x顯然是新堆(子堆)的根節點,但是y在哪裏我們並不知道。
為了滿足左偏樹的性質(不提供證明),我們將x的右子堆和y堆合並,最後按照它們的dis交換一下左右兒子即可。
插入:
將插入元素看成堆的話,與合並相同。
刪除最小節點:
將根節點刪除後,其左右子堆合並即可。
————————————————————————————————
學到這裏代碼實現不是很難,這裏提供板子題目:
洛谷P3377:[模板]左偏樹(可並堆):https://www.luogu.org/problemnew/show/3377
代碼如下:
#include<cstdio> #include<cmath> #include<algorithm> #include<cstring> #include<iostream> #include<cctype> #include<queue> using namespace std; const int N=1e5+3; inline int read(){ int X=0,w=0;char ch=0; while(!isdigit(ch)){w|=ch==‘-‘;ch=getchar();} while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar(); return w?-X:X; } int fa[N]; struct tree{ int l,r; int dis,val; }tr[N]; int merge(int x,int y){//兩個堆的根,返回新堆的根 if(x==0||y==0)return x+y;//其中一個是空堆 if(tr[x].val>tr[y].val||(tr[x].val==tr[y].val&&x>y)) swap(x,y);//讓x的價值比y的價值小,這樣x就在y上面 tr[x].r=merge(tr[x].r,y);//合並x右樹和y fa[tr[x].r]=x; if(tr[tr[x].l].dis<tr[tr[x].r].dis) swap(tr[x].l,tr[x].r);//讓右兒子更接近外節點 tr[x].dis=tr[tr[x].r].dis+1;//根據右兒子dis更新節點dis return x; } int get(int x){ while(fa[x])x=fa[x]; return x; } void del(int x){ tr[x].val=-1; fa[tr[x].l]=fa[tr[x].r]=0; merge(tr[x].l,tr[x].r); return; } int main(){ int n=read(); int m=read(); for(int i=1;i<=n;i++)tr[i].val=read(); for(int i=1;i<=m;i++){ int c=read(); if(c==1){ int x=read(); int y=read(); if(tr[x].val==-1||tr[y].val==-1||x==y)continue; int nx=get(x); int ny=get(y); merge(nx,ny); }else{ int x=read(); if(tr[x].val==-1){ puts("-1"); }else{ int y=get(x); printf("%d\n",tr[y].val); del(y); } } } return 0; }
模板:左偏樹