// Return a new empty node.intnew_node(){intx=top?pool[--top]:++id;sz[x]=val[x]=ch[x][0]=ch[x][1]=0;returnx;}// Release a node for later use.voiddel_node(int&x){pool[top++]=x;x=0;}// Return a new leaf node of value v.intnew_leaf(intv){intx=new_node();val[x]=v;sz[x]=1;returnx;}// Return a new node with subtrees x and y.intjoin(intx,inty){intz=new_node();ch[z][0]=x;ch[z][1]=y;push_up(z);returnz;}// Return subtrees of x and release x.autocut(int&x){inty=ch[x][0];intz=ch[x][1];del_node(x);returnstd::make_pair(y,z);}
// Rotate the subtree at x such that ch[x][r] is the new root.voidrotate(int&x,boolr){inty=ch[x][r];ch[x][r]=ch[y][!r];ch[y][!r]=x;push_up(x);push_up(y);x=y;}
1 2 3 4 5 6 7 8 9101112
// Rotate the subtree at x such that ch[x][r] is the new root.voidrotate(int&x,boolr){inta,b,c,d;std::tie(a,b)=cut(x);if(r){std::tie(c,d)=cut(b);x=join(join(a,c),d);}else{std::tie(c,d)=cut(a);x=join(c,join(d,b));}}
// Check whether a subtree of weight SX is too heavy// in a tree of weight SX + SY.booltoo_heavy(intsx,intsy){// or sx > sy * 3;returnsy<ALPHA*(sx+sy);}// Check if ch[x][!r] is too heavy so that a double rotation is needed.boolneed_double_rotation(intx,boolr){// or sz[ch[x][!r]] > sz[ch[x][r]] * 2;returnsz[ch[x][!r]]>sz[x]/(2-ALPHA);}// Balance the subtree at x;voidbalance(int&x){if(sz[x]==1)return;boolr=sz[ch[x][1]]>sz[ch[x][0]];if(!too_heavy(sz[ch[x][r]],sz[ch[x][!r]]))return;if(need_double_rotation(ch[x][r],r)){rotate(ch[x][r],!r);}rotate(x,r);}
实现了维护平衡的策略后,合并两树的算法就非常简单。仍然设 ,合并的策略如下:
如果右子树 是空的,直接返回左子树 ;
如果左右子树 和 已经平衡,即 ,直接连接两子树;
否则,将 的右子树 与 合并,将左子树 与它们合并的结果连接,并调整新树的平衡。
参考实现如下:
参考代码
1 2 3 4 5 6 7 8 9101112131415161718
// Merge two subtrees.intmerge(intx,inty){if(!x||!y)returnx|y;inta,b;if(too_heavy(sz[x],sz[y])){std::tie(a,b)=cut(x);intz=join(a,merge(b,y));balance(z);returnz;}elseif(too_heavy(sz[y],sz[x])){std::tie(a,b)=cut(y);intz=join(merge(x,a),b);balance(z);returnz;}else{returnjoin(x,y);}}
// Merge two subtrees.intmerge(intx,inty){if(!x||!y)returnx|y;inta,b,c,d;if(too_heavy(sz[x],sz[y])){std::tie(a,b)=cut(x);if(too_heavy(sz[b]+sz[y],sz[a])){std::tie(c,d)=cut(b);returnmerge(merge(a,c),merge(d,y));}else{returnmerge(a,merge(b,y));}}elseif(too_heavy(sz[y],sz[x])){std::tie(a,b)=cut(y);if(too_heavy(sz[a]+sz[x],sz[b])){std::tie(c,d)=cut(a);returnmerge(merge(x,c),merge(d,b));}else{returnmerge(merge(x,a),b);}}else{returnjoin(x,y);}}
利用该合并策略,同样可以很容易实现树的平衡维护:失衡时,直接合并左右子树即可。
参考代码
1 2 3 4 5 6 7 8 910
// Balance the subtree at x;voidbalance(int&x){if(sz[x]==1)return;if(too_heavy(sz[ch[x][0]],sz[ch[x][1]])||too_heavy(sz[ch[x][1]],sz[ch[x][0]])){inta,b;std::tie(a,b)=cut(x);x=merge(a,b);}}
// Build the tree for the interval [ll, rr].intbuild(intll,intrr){if(ll==rr)returnnew_leaf(ll);intmm=(ll+rr)/2;returnjoin(build(ll,mm),build(mm+1,rr));}
// Insert v to the subtree at x.voidinsert(int&x,intv){if(!x){x=new_leaf(v);}elseif(sz[x]==1){boolr=v>=val[x];ch[x][r]=new_leaf(v);ch[x][!r]=new_leaf(val[x]);push_up(x);}else{boolr=v>val[ch[x][0]];insert(ch[x][r],v);push_up(x);balance(x);}}// Insert v.voidinsert(intv){insert(rt,v);}// Remove v from the subtree at x.boolremove(int&x,intv){if(!x)returnfalse;if(sz[x]==1){if(val[x]==v){del_node(x);returntrue;}else{returnfalse;}}else{boolr=v>val[ch[x][0]];boolsucc=remove(ch[x][r],v);if(!ch[x][r]){x=ch[x][!r];}else{push_up(x);balance(x);}returnsucc;}}// Remove v.boolremove(intv){returnremove(rt,v);}
// Count the number of nodes less than v in the subtree at x.intcount_less_than(intx,intv){if(!x)return0;intres=0;while(sz[x]>1){if(val[ch[x][0]]<v){res+=sz[ch[x][0]];x=ch[x][1];}else{x=ch[x][0];}}returnres+(val[x]<v?1:0);}// Find the rank of v.intfind_rank(intv){returncount_less_than(rt,v)+1;}
时间复杂度为 。
根据排名查询
依然是利用线段树上二分的思想,只不过这里比较的是节点的权重。
参考实现如下:
参考代码
1 2 3 4 5 6 7 8 910111213141516
// Find the k-th element in the subtree at x.// It is guaranteed that such an element exists.intfind_kth(intx,intk){while(sz[x]>1){if(sz[ch[x][0]]>=k){x=ch[x][0];}else{k-=sz[ch[x][0]];x=ch[x][1];}}returnval[x];}// Find the k-th element.intfind_kth(intk){returnk>sz[rt]||k<=0?-1:find_kth(rt,k);}
时间复杂度为 。
查找前驱、后继
以上两种功能结合即可。
参考实现如下:
参考代码
12345
// Find the predecessor of v.intfind_prev(intv){returnfind_kth(find_rank(v)-1);}// Find the successor of v.intfind_next(intv){returnfind_kth(find_rank(v+1));}
// Split the subtree at x.// The left half will have k elements.std::pair<int,int>split(intx,intk){if(!x)return{0,0};if(!k)return{0,x};if(k==sz[x])return{x,0};inta,b;std::tie(a,b)=cut(x);if(k<=sz[a]){intll,rr;std::tie(ll,rr)=split(a,k);return{ll,merge(rr,b)};}else{intll,rr;std::tie(ll,rr)=split(b,k-sz[a]);return{merge(a,ll),rr};}}
#include<iostream>#include<tuple>// Change here to use different strategies to balance and to merge.#define BALANCE_BY_ROTATING 1#define ROTATE_BY_JOINING 0constexprintN=2e6;constexprdoubleALPHA=0.292;intid,rt,ch[N][2],sz[N],val[N];intpool[N],top;voidpush_up(intx){sz[x]=sz[ch[x][0]]+sz[ch[x][1]];val[x]=val[ch[x][1]];}// Return a new empty node.intnew_node(){intx=top?pool[--top]:++id;sz[x]=val[x]=ch[x][0]=ch[x][1]=0;returnx;}// Release a node for later use.voiddel_node(int&x){pool[top++]=x;x=0;}// Return a new leaf node of value v.intnew_leaf(intv){intx=new_node();val[x]=v;sz[x]=1;returnx;}// Return a new node with subtrees x and y.intjoin(intx,inty){intz=new_node();ch[z][0]=x;ch[z][1]=y;push_up(z);returnz;}// Return subtrees of x and release x.autocut(int&x){inty=ch[x][0];intz=ch[x][1];del_node(x);returnstd::make_pair(y,z);}// Check whether a subtree of weight SX is too heavy// in a tree of weight SX + SY.booltoo_heavy(intsx,intsy){// or sx > sy * 3;returnsy<ALPHA*(sx+sy);}#if BALANCE_BY_ROTATING#if ROTATE_BY_JOINING// Rotate the subtree at x such that ch[x][r] is the new root.voidrotate(int&x,boolr){inta,b,c,d;std::tie(a,b)=cut(x);if(r){std::tie(c,d)=cut(b);x=join(join(a,c),d);}else{std::tie(c,d)=cut(a);x=join(c,join(d,b));}}#else// Rotate the subtree at x such that ch[x][r] is the new root.voidrotate(int&x,boolr){inty=ch[x][r];ch[x][r]=ch[y][!r];ch[y][!r]=x;push_up(x);push_up(y);x=y;}#endif// Check if ch[x][!r] is too heavy so that a double rotation is needed.boolneed_double_rotation(intx,boolr){// or sz[ch[x][!r]] > sz[ch[x][r]] * 2;returnsz[ch[x][!r]]>sz[x]/(2-ALPHA);}// Balance the subtree at x;voidbalance(int&x){if(sz[x]==1)return;boolr=sz[ch[x][1]]>sz[ch[x][0]];if(!too_heavy(sz[ch[x][r]],sz[ch[x][!r]]))return;if(need_double_rotation(ch[x][r],r)){rotate(ch[x][r],!r);}rotate(x,r);}// Merge two subtrees.intmerge(intx,inty){if(!x||!y)returnx|y;inta,b;if(too_heavy(sz[x],sz[y])){std::tie(a,b)=cut(x);intz=join(a,merge(b,y));balance(z);returnz;}elseif(too_heavy(sz[y],sz[x])){std::tie(a,b)=cut(y);intz=join(merge(x,a),b);balance(z);returnz;}else{returnjoin(x,y);}}#else// Merge two subtrees.intmerge(intx,inty){if(!x||!y)returnx|y;inta,b,c,d;if(too_heavy(sz[x],sz[y])){std::tie(a,b)=cut(x);if(too_heavy(sz[b]+sz[y],sz[a])){std::tie(c,d)=cut(b);returnmerge(merge(a,c),merge(d,y));}else{returnmerge(a,merge(b,y));}}elseif(too_heavy(sz[y],sz[x])){std::tie(a,b)=cut(y);if(too_heavy(sz[a]+sz[x],sz[b])){std::tie(c,d)=cut(a);returnmerge(merge(x,c),merge(d,b));}else{returnmerge(merge(x,a),b);}}else{returnjoin(x,y);}}// Balance the subtree at x;voidbalance(int&x){if(sz[x]==1)return;if(too_heavy(sz[ch[x][0]],sz[ch[x][1]])||too_heavy(sz[ch[x][1]],sz[ch[x][0]])){inta,b;std::tie(a,b)=cut(x);x=merge(a,b);}}#endif// Insert v to the subtree at x.voidinsert(int&x,intv){if(!x){x=new_leaf(v);}elseif(sz[x]==1){boolr=v>=val[x];ch[x][r]=new_leaf(v);ch[x][!r]=new_leaf(val[x]);push_up(x);}else{boolr=v>val[ch[x][0]];insert(ch[x][r],v);push_up(x);balance(x);}}// Insert v.voidinsert(intv){insert(rt,v);}// Remove v from the subtree at x.boolremove(int&x,intv){if(!x)returnfalse;if(sz[x]==1){if(val[x]==v){del_node(x);returntrue;}else{returnfalse;}}else{boolr=v>val[ch[x][0]];boolsucc=remove(ch[x][r],v);if(!ch[x][r]){x=ch[x][!r];}else{push_up(x);balance(x);}returnsucc;}}// Remove v.boolremove(intv){returnremove(rt,v);}// Count the number of nodes less than v in the subtree at x.intcount_less_than(intx,intv){if(!x)return0;intres=0;while(sz[x]>1){if(val[ch[x][0]]<v){res+=sz[ch[x][0]];x=ch[x][1];}else{x=ch[x][0];}}returnres+(val[x]<v?1:0);}// Find the rank of v.intfind_rank(intv){returncount_less_than(rt,v)+1;}// Find the k-th element in the subtree at x.// It is guaranteed that such an element exists.intfind_kth(intx,intk){while(sz[x]>1){if(sz[ch[x][0]]>=k){x=ch[x][0];}else{k-=sz[ch[x][0]];x=ch[x][1];}}returnval[x];}// Find the k-th element.intfind_kth(intk){returnk>sz[rt]||k<=0?-1:find_kth(rt,k);}// Find the predecessor of v.intfind_prev(intv){returnfind_kth(find_rank(v)-1);}// Find the successor of v.intfind_next(intv){returnfind_kth(find_rank(v+1));}intmain(){intn;std::cin>>n;for(;n;--n){intop,x;std::cin>>op>>x;switch(op){case1:insert(x);break;case2:remove(x);break;case3:std::cout<<find_rank(x)<<'\n';break;case4:std::cout<<find_kth(x)<<'\n';break;case5:std::cout<<find_prev(x)<<'\n';break;case6:std::cout<<find_next(x)<<'\n';break;}}return0;}
#include<iostream>#include<tuple>// Change here to use different strategies to balance and to merge.#define BALANCE_BY_ROTATING 0#define ROTATE_BY_JOINING 1constexprintN=2e5;constexprdoubleALPHA=0.292;intid,rt,ch[N][2],sz[N],val[N],lz[N];intpool[N],top;voidpush_up(intx){sz[x]=sz[ch[x][0]]+sz[ch[x][1]];}voidlazy_reverse(intx){if(!x)return;std::swap(ch[x][0],ch[x][1]);lz[x]^=1;}voidpush_down(intx){if(lz[x]){lazy_reverse(ch[x][0]);lazy_reverse(ch[x][1]);lz[x]=0;}}// Return a new empty node.intnew_node(){intx=top?pool[--top]:++id;sz[x]=val[x]=ch[x][0]=ch[x][1]=0;returnx;}// Release a node for later use.voiddel_node(int&x){pool[top++]=x;x=0;}// Return a new leaf node of value v.intnew_leaf(intv){intx=new_node();val[x]=v;sz[x]=1;returnx;}// Return a new node with subtrees x and y.intjoin(intx,inty){intz=new_node();ch[z][0]=x;ch[z][1]=y;push_up(z);returnz;}// Return subtrees of x and release x.autocut(int&x){push_down(x);inty=ch[x][0];intz=ch[x][1];del_node(x);returnstd::make_pair(y,z);}// Check whether a subtree of weight SX is too heavy// in a tree of weight SX + SY.booltoo_heavy(intsx,intsy){// or sx > sy * 3;returnsy<ALPHA*(sx+sy);}#if BALANCE_BY_ROTATING#if ROTATE_BY_JOINING// Rotate the subtree at x such that ch[x][r] is the new root.voidrotate(int&x,boolr){inta,b,c,d;std::tie(a,b)=cut(x);if(r){std::tie(c,d)=cut(b);x=join(join(a,c),d);}else{std::tie(c,d)=cut(a);x=join(c,join(d,b));}}#else// Rotate the subtree at x such that ch[x][r] is the new root.voidrotate(int&x,boolr){inty=ch[x][r];ch[x][r]=ch[y][!r];ch[y][!r]=x;push_up(x);push_up(y);x=y;}#endif// Check if ch[x][!r] is too heavy so that a double rotation is needed.boolneed_double_rotation(intx,boolr){// or sz[ch[x][!r]] > sz[ch[x][r]] * 2;returnsz[ch[x][!r]]>sz[x]/(2-ALPHA);}// Balance the subtree at x;voidbalance(int&x){if(sz[x]==1)return;push_down(x);boolr=sz[ch[x][1]]>sz[ch[x][0]];if(!too_heavy(sz[ch[x][r]],sz[ch[x][!r]]))return;push_down(ch[x][r]);if(need_double_rotation(ch[x][r],r)){push_down(ch[ch[x][r]][!r]);rotate(ch[x][r],!r);}rotate(x,r);}// Merge two subtrees.intmerge(intx,inty){if(!x||!y)returnx|y;inta,b;if(too_heavy(sz[x],sz[y])){std::tie(a,b)=cut(x);intz=join(a,merge(b,y));balance(z);returnz;}elseif(too_heavy(sz[y],sz[x])){std::tie(a,b)=cut(y);intz=join(merge(x,a),b);balance(z);returnz;}else{returnjoin(x,y);}}#else// Merge two subtrees.intmerge(intx,inty){if(!x||!y)returnx|y;inta,b,c,d;if(too_heavy(sz[x],sz[y])){std::tie(a,b)=cut(x);if(too_heavy(sz[b]+sz[y],sz[a])){std::tie(c,d)=cut(b);returnmerge(merge(a,c),merge(d,y));}else{returnmerge(a,merge(b,y));}}elseif(too_heavy(sz[y],sz[x])){std::tie(a,b)=cut(y);if(too_heavy(sz[a]+sz[x],sz[b])){std::tie(c,d)=cut(a);returnmerge(merge(x,c),merge(d,b));}else{returnmerge(merge(x,a),b);}}else{returnjoin(x,y);}}// Balance the subtree at x;voidbalance(int&x){if(sz[x]==1)return;if(too_heavy(sz[ch[x][0]],sz[ch[x][1]])||too_heavy(sz[ch[x][1]],sz[ch[x][0]])){inta,b;std::tie(a,b)=cut(x);x=merge(a,b);}}#endif// Split the subtree at x.// The left half will have k elements.std::pair<int,int>split(intx,intk){if(!x)return{0,0};if(!k)return{0,x};if(k==sz[x])return{x,0};inta,b;std::tie(a,b)=cut(x);if(k<=sz[a]){intll,rr;std::tie(ll,rr)=split(a,k);return{ll,merge(rr,b)};}else{intll,rr;std::tie(ll,rr)=split(b,k-sz[a]);return{merge(a,ll),rr};}}// Reverse the interval [l, r].voidreverse(intl,intr){intll,rr;std::tie(rt,rr)=split(rt,r);std::tie(ll,rt)=split(rt,l-1);lazy_reverse(rt);rt=merge(ll,merge(rt,rr));}// Output the subtree at x.voidprint(intx){if(sz[x]==1){std::cout<<val[x]<<' ';}else{push_down(x);print(ch[x][0]);print(ch[x][1]);}}// Output the tree.voidprint(){print(rt);std::cout<<'\n';}// Build the tree for the interval [ll, rr].intbuild(intll,intrr){if(ll==rr)returnnew_leaf(ll);intmm=(ll+rr)/2;returnjoin(build(ll,mm),build(mm+1,rr));}intmain(){intn,m;std::cin>>n>>m;rt=build(1,n);for(;m;--m){intl,r;std::cin>>l>>r;reverse(l,r);}print();return0;}
Nievergelt, J.; Reingold, E. M. (1973). "Binary Search Trees of Bounded Balance". SIAM Journal on Computing. 2: 33–43.
Blum, Norbert; Mehlhorn, Kurt (1980). "On the average number of rebalancing operations in weight-balanced trees". Theoretical Computer Science. 11 (3): 303–320.
Hirai, Y.; Yamamoto, K. (2011). "Balancing weight-balanced trees". Journal of Functional Programming. 21 (3): 287.
Blelloch, Guy E.; Ferizovic, Daniel; Sun, Yihan (2016), "Just Join for Parallel Ordered Sets", Symposium on Parallel Algorithms and Architectures, Proc. of 28th ACM Symp. Parallel Algorithms and Architectures (SPAA 2016), ACM, pp. 253–264.
Straka, Milan. (2011). "Adams’Trees Revisited: Correctness Proof and Efficient Implementation." International Symposium on Trends in Functional Programming. Berlin, Heidelberg: Springer Berlin Heidelberg.