红黑树(138行)

代码的红黑树部分138行。

本文的图中,红/黑点代表红/黑色节点,蓝点代表无关点(或者子树),绿点代表无所谓颜色的点。

红黑树的性质:

  1. 是一颗二叉搜索树
  2. 每个节点是红色或者黑色
  3. 根和NULL节点是黑色节点
  4. 红色节点的儿子一定是黑色节点
  5. 根到每个NULL节点的路径上的黑色节点个数相等(叫做黑高度)

根据这些性质可以推出,树深度小于等于二倍黑高度,是\(O(\log n)\)级别

旋转

图中从左到右是右旋,从右到左是左旋,不细说了。

旋转

inline void rotate(int x) {
	static int y,z,k,w;
	y=tr[x].fa,z=tr[y].fa,k=tr[y].son[1]==x,w=tr[x].son[k^1];
	if(w)tr[w].fa=y;
	tr[y].son[k]=w,tr[x].son[k^1]=y,tr[z].son[tr[z].son[1]==y]=x,tr[y].fa=x,tr[x].fa=z;
	push_up(y),push_up(x);
}

插入

当原树为空时,直接将根节点赋值为一个新黑色节点即可

其余情况考虑插入一个红色节点,这样就只有可能和性质4发生冲突(即有可能出现双红节点)

之后对这个节点进行双红修正即可。

inline int push(int x) {
	if(!rt)return rt=newnode(x,0);
	for(int i=rt,id=0; 1; i=tr[i].son[id]) {
		++tr[i].siz;
		if(tr[i].val==x) //值相同的点合并
			return ++tr[i].cnt,i;
		id=tr[i].val<x;
		if(!tr[i].son[id]) {
			tr[i].son[id]=newnode(x,1),tr[tr[i].son[id]].fa=i;
			solve_double_red(tr[i].son[id]);
			return tr[i].son[id];
		}
	}
}

双红修正

用一个函数solve_double_red(x)来解决

这个函数的意义是:\(x\)的子树内已经修正完毕,但\(x,fa(x)\)有可能是双红,需要接着修正。

首先当\(x\)为根或\(x\)的父亲为根时,直接将根节点染为黑色即可。

记得在函数里判断不是双红的情况(直接return

记函数\(id(x)\)\(id(x)=0\)\(x\)是其父亲的左儿子,\(id(x)=1\)\(x\)是其父亲的右儿子。

设现在要修正的节点为\(x\),其父亲节点为\(y\)\(y\)的节点(\(x\)的祖父)为\(z\)\(z\)的另一个儿子(\(x\)的叔父)为\(w\)

\(w\)为黑色节点(或为空)时:(可以发现这两种都是以中间值为根)

  1. \(id(x)=id(y)\),那么rotate(y)之后把\(y\)染成黑色,\(z\)染成红色

  2. \(id(x)\not=id(y)\),那么rotate(x),swap(x,y)可以转换为情况\(1\)

双红修正

\(w\)为红色节点时:将\(y,w\)染为黑色,\(z\)染为红色,递归solve_double_red(z)即可。

双红修正(2)

void solve_double_red(int x) {
	if(x==rt||tr[x].fa==rt) {
		tr[rt].col=0;
		return;
	}
	if(!tr[tr[x].fa].col) return;//判断双红
	static int y,z,w;
	y=tr[x].fa,z=tr[y].fa,w=tr[z].son[tr[z].son[0]==y];
	if(!tr[w].col) {//w是黑点
		if((tr[y].son[1]==x)^(tr[z].son[1]==y))//id(x)!=id(y)
			rotate(x),swap(x,y);
		rotate(y),tr[z].col=1,tr[y].col=0;//id(x)=id(y)
		return;
	}
	tr[y].col=tr[w].col=0,tr[z].col=1;
	return solve_double_red(z);//尾递归优化
}

删除

首先肯定要找到删除的数对应的节点\(x\)

如果\(x\)是唯一的点,直接删除后将根赋值为\(0\),其余情况下:

找到当前数的真后继对应的节点\(y\),可以交换\(x,y\)的数据,转化为删除节点\(y\)

这样我们删除的节点一定在最后一层。

考虑删除一个在最后一层的节点\(x\)

\(x\)为红色节点,可以直接删除,不会和任何性质有矛盾

\(x\)为黑色节点,删除后会和性质5矛盾,所以删除前要用双黑修正。

inline void pop(int x) {
	int i=rt;
	for(; tr[i].val^x&&tr[i].son[tr[i].val<x]; i=tr[i].son[tr[i].val<x])
		--tr[i].siz;//先找到值x对应的点
	--tr[i].siz,--tr[i].cnt;
	if(tr[i].cnt) return;
	if(i==rt&&!tr[i].son[0]&&!tr[i].son[1])
		return rt=0,Q.push(i);//删后是空树
	for(x=i; tr[x].son[0]||tr[x].son[1]; ) {//找真后继
		static int y;
		if(!tr[x].son[0]) y=tr[x].son[1];
		else if(!tr[x].son[1]) y=tr[x].son[0];
		else for(y=tr[x].son[1]; tr[y].son[0]; y=tr[y].son[0]);
		swap(tr[x].val,tr[y].val),swap(tr[x].cnt,tr[y].cnt);
		x=y;//可以发现这样删除之后仍满足二叉搜索树的性质
	}
	Q.push(x);//节点回收
	if(!tr[x].col) solve_double_black(x);//删除的节点是黑色,要进行双黑修正
	if(tr[x].fa)tr[tr[x].fa].son[tr[tr[x].fa].son[1]==x]=0;
	for(i=tr[x].fa; i; i=tr[i].fa) push_up(i);//记得push_up
}

双黑修正

用一个函数solve_double_black(x)来解决

这个函数的意义是:\(x\)的子树内的黑高度相等,但\(x\)子树内的黑高度比它兄弟子树内的黑高度小\(1\),需要接着修正。

\(x\)为根时(整棵树的黑高度都减去\(1\)),就直接返回。

\(y\)\(x\)的父亲,\(z\)\(x\)的兄弟。

\(z\)为红色节点:rotate(z)并交换\(z,y\)的颜色,可以转换为\(z\)为黑的情况。
双黑修正

\(z\)为黑色节点且存在红色儿子\(w\):(还是中间值做根)

  1. \(id(w)=id(z)\),那么rotate(z)之后把\(z\)染成\(y\)的颜色,\(y,w\)染成黑色
    双黑修正(2)
  2. \(id(w)\not=id(z)\),那么rotate(w),rotate(w)后把\(w\)染成\(y\)的颜色,\(y,z\)染成黑色
    双黑修正(3)

\(z\)为黑色节点且无红色儿子:

  1. \(y\)为红色,染红\(z\),染黑\(y\)就行。
    双黑修正(4)
  2. \(y\)为黑色,染红\(z\),递归solve_double_black(y)
    双黑修正(5)
void solve_double_black(int x) {
	if(x==rt) {//判根
		tr[x].col=0;
		return;
	}
	static int y,z,w,k;
	y=tr[x].fa,k=tr[y].son[0]==x,z=tr[y].son[k];
	if(tr[z].col) {//z为红点
		tr[z].col=0,tr[y].col=1,rotate(z);
		y=tr[x].fa,k=tr[y].son[0]==x,z=tr[y].son[k];//旋转后要更新值
	}
	w=tr[z].son[k];//优先选id相同的红节点
	if(tr[w].col) {
		tr[z].col=tr[y].col,tr[w].col=tr[y].col=0;
		return rotate(z);
	}
	w=tr[z].son[k^1];
	if(tr[w].col) {
		tr[w].col=tr[y].col,tr[z].col=tr[y].col=0;
		return rotate(w),rotate(w);
	}
	if(tr[y].col) {
		tr[y].col=0,tr[z].col=1;
		return;
	}
	return tr[z].col=1,solve_double_black(y);
}

剩下的部分其实和普通的二叉查找树是一样的,直接放P6136 【模板】普通平衡树(数据加强版)
完整代码吧:(最慢的点512ms)

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1100005;
inline int read() {
	static int x,c=getchar(),f;
	for(f=1; c<=47||c>=58; c=getchar())f=f&&(c^45);
	for(x=0; c>=48&&c<=57; c=getchar())x=(x<<3)+(x<<1)+(c&15);
	return f?x:-x;
}
struct node {
	int fa,son[2],siz,cnt,val,col;
} tr[MAXN];
queue<int>Q;
int tcnt;
class RBT {
	public:	
		int rt;
		inline int newnode(int v,int c) {
			static int x;
			if(Q.empty()) x=++tcnt;
			else x=Q.front(),Q.pop();
			tr[x]={0,0,0,1,1,v,c};
			return x;
		}
		inline void push_up(int x) {
			tr[x].siz=tr[tr[x].son[0]].siz+tr[tr[x].son[1]].siz+tr[x].cnt;
		}
		inline void rotate(int x) {
			static int y,z,k,w;
			y=tr[x].fa,z=tr[y].fa,k=tr[y].son[1]==x,w=tr[x].son[k^1];
			if(w)tr[w].fa=y;
			if(z)tr[z].son[tr[z].son[1]==y]=x;
			else rt=x;
			tr[y].son[k]=w,tr[x].son[k^1]=y,tr[y].fa=x,tr[x].fa=z;
			push_up(y),push_up(x);
		}
		void solve_double_red(int x) {
			if(x==rt||tr[x].fa==rt) {
				tr[rt].col=0;
				return;
			}
			if(!tr[tr[x].fa].col) return;
			static int y,z,w;
			y=tr[x].fa,z=tr[y].fa,w=tr[z].son[tr[z].son[0]==y];
			if(!tr[w].col) {
				if((tr[y].son[1]==x)^(tr[z].son[1]==y))
					rotate(x),swap(x,y);
				rotate(y),tr[z].col=1,tr[y].col=0;
				return;
			}
			tr[y].col=tr[w].col=0,tr[z].col=1;
			return solve_double_red(z);
		}
		inline int push(int x) {
			if(!rt)return rt=newnode(x,0);
			for(int i=rt,id=0; 1; i=tr[i].son[id]) {
				++tr[i].siz;
				if(tr[i].val==x) 
					return ++tr[i].cnt,i;
				id=tr[i].val<x;
				if(!tr[i].son[id]) {
					tr[i].son[id]=newnode(x,1),tr[tr[i].son[id]].fa=i;
					solve_double_red(tr[i].son[id]);
					return tr[i].son[id];
				}
			}
		}
		inline int rnk(int x) {
			int res=1,i=rt;
			while(i)
				if(tr[i].val==x) return res+tr[tr[i].son[0]].siz;
				else if(x<tr[i].val)i=tr[i].son[0];
				else res+=tr[tr[i].son[0]].siz+tr[i].cnt,i=tr[i].son[1];
			return res;
		}
		inline int kth(int x) {
			int res=0,i=rt;
			while(1)
				if(res+tr[tr[i].son[0]].siz<x&&x<=res+tr[tr[i].son[0]].siz+tr[i].cnt)
					return tr[i].val;
				else if(x<=res+tr[tr[i].son[0]].siz)
					i=tr[i].son[0];
				else res+=tr[tr[i].son[0]].siz+tr[i].cnt,i=tr[i].son[1];
		}
		inline int pre(int x) {
			int res=0,i=rt;
			while(i)
				if(tr[i].val>=x)i=tr[i].son[0];
				else res=tr[i].val,i=tr[i].son[1];
			return res;
		}
		inline int suf(int x) {
			int res=0,i=rt;
			while(i)
				if(tr[i].val<=x)i=tr[i].son[1];
				else res=tr[i].val,i=tr[i].son[0];
			return res;
		}
		void solve_double_black(int x) {
			if(x==rt) {
				tr[x].col=0;
				return;
			}
			static int y,z,w,k;
			y=tr[x].fa,k=tr[y].son[0]==x,z=tr[y].son[k];
			if(tr[z].col) {
				tr[z].col=0,tr[y].col=1,rotate(z);
				y=tr[x].fa,k=tr[y].son[0]==x,z=tr[y].son[k];
			}
			w=tr[z].son[k];
			if(tr[w].col) {
				tr[z].col=tr[y].col,tr[w].col=tr[y].col=0;
				return rotate(z);
			}
			w=tr[z].son[k^1];
			if(tr[w].col) {
				tr[w].col=tr[y].col,tr[z].col=tr[y].col=0;
				return rotate(w),rotate(w);
			}
			if(tr[y].col) {
				tr[y].col=0,tr[z].col=1;
				return;
			}
			return tr[z].col=1,solve_double_black(y);
		}
		inline void pop(int x) {
			int i=rt;
			for(; tr[i].val^x&&tr[i].son[tr[i].val<x]; i=tr[i].son[tr[i].val<x])
				--tr[i].siz;
			--tr[i].siz,--tr[i].cnt;
			if(tr[i].cnt) return;
			if(i==rt&&!tr[i].son[0]&&!tr[i].son[1])
				return rt=0,Q.push(i);
			for(x=i; tr[x].son[0]||tr[x].son[1]; ) {
				static int y;
				if(!tr[x].son[0]) y=tr[x].son[1];
				else if(!tr[x].son[1]) y=tr[x].son[0];
				else for(y=tr[x].son[1]; tr[y].son[0]; y=tr[y].son[0]);
				swap(tr[x].val,tr[y].val),swap(tr[x].cnt,tr[y].cnt);
				x=y;
			}
			Q.push(x);
			if(!tr[x].col) solve_double_black(x);
			if(tr[x].fa)tr[tr[x].fa].son[tr[tr[x].fa].son[1]==x]=0;
			for(i=tr[x].fa; i; i=tr[i].fa) push_up(i);
		}
} T;
int n,m,lst,ans;
int main() {
	n=read(),m=read();
	for(int i=1; i<=n; ++i)T.push(read());
	for(int i=1,op,x; i<=m; ++i) {
		op=read(),x=read()^lst;
		if(op==1)T.push(x);
		else if(op==2)T.pop(x);
		else if(op==3)lst=T.rnk(x),ans^=lst;
		else if(op==4)lst=T.kth(x),ans^=lst;
		else if(op==5)lst=T.pre(x),ans^=lst;
		else if(op==6)lst=T.suf(x),ans^=lst;
	}
	printf("%d\n",ans);
	return 0;
}
posted @ 2022-10-22 09:50  mod998244353  阅读(46)  评论(0编辑  收藏  举报
Live2D