其他分享
首页 > 其他分享> > 【模板】有旋Treap

【模板】有旋Treap

作者:互联网

\(update\) \(at\) \(2022.6.16\) 修改了些晦涩难懂的地方。

调了一周,今天总算调出来了。

概述:

$ Treap = Tree + Heap $,其既有二叉查找树 \(BST\) 的性质,又有堆 \(Heap\) 的性质,于是有能维护排名,有能保证深度在 \(Θ(logN)\) 的量级。

BST:

二叉查找树,满足保证根左侧子树的所有节点比根小,右侧的所有节点比根大的树(没有相同节点)。

操作:

总结:

\(BST\) 支持 \(Treap\) 的所有一般操作,功能齐全,实现简单,在随机数据下也比 \(Treap\) 等平衡树快很多。

但 \(BST\) 毕竟不能维护树的平衡, \(BST\) 的复杂度取决于它的平均深度,在特定数据下树会退化为链,使深度为线性,于是单次操作的复杂度会提升到 \(Θ(N)\) ,明显不够优。

于是,我们需要引入 \(Treap\) 的下一个性质: \(Heap\)

Heap:

即堆,是一种保证任意节点的左右儿子都比自身小的完全二叉树,其深度始终保持在 \(logN\) 的数量级,刚好符合了我们的需求。

操作:

Treap:

\(Treap\) 就是集 \(BST\) 、 \(Heap\) 二者的性质于一身,即能够支持 \(BST\) 的操作,有能够保证 \(Heap\) 的深度。

可惜的是, \(BST\) 和 \(Heap\) 的性质似乎有些矛盾,前者是左子树 \(<\) 根 \(<\) 右子树,后者是根 \(<\) 左儿子 \(<\) 右儿子。

其实 \(Treap\) 的本质还是 \(BST\) ,对于任意节点,保证根左侧子树的所有节点比根小,右侧的所有节点比根大的树(没有相同节点)。
我们只是利用堆的性质,赋予每一个节点一个随机值,按照随机值维护堆的形状。
于是我们需要一个操作,既能保持 \(BST\) 的性质,又能够将根节点与儿子替换,于是我们需要 \(Treap\) 的核心——旋转操作

旋转:

\(rotate\) ,即旋转操作,分为 \(zig\)左旋和 \(zag\)右旋,其思想是一致的,也可以统一实现,故一起介绍。
\(rotate\) 的目标是将一个儿子移到根处,并且在此过程中保持BST的性质。

实现:

struct Tree{
	int rd; //i节点的一个随机值,是在堆中的关键字
	int val; //i节点的关键字
	int num; //由于可能有重复,所以存储的是i节点关键字的个数
	int size; //以i为根的子树的节点数
	int ch[2]; //存储i节点的儿子,ch[i][0]表示左儿子,ch[i][1]表示右儿子

	Tree(){
		rd = val = num = size = 0;
		ch[0] = ch[1] = 0;
	}
}tr[MAXN];

为了方便,展示下宏定义

#define lson(x) tr[x].ch[0]
#define rson(x) tr[x].ch[1]
  void Pushup(int rt){
      tr[rt].size = tr[lson(rt)].size + tr[rson(rt)].size + tr[rt].num;
  }
  void Rotate(int &rt, int d){ //d = 0时是右儿子旋上来,即左旋, d = 1是左儿子旋上来,即右旋
      int son = tr[rt].ch[d ^ 1];

		tr[rt].ch[d ^ 1] = tr[son].ch[d];
		tr[son].ch[d] = rt;

		Pushup(rt);
		Pushup(son);

		rt = son;
	} //之前这玩意简直不是人写出来的 
  void Insert(int &rt, int val){
      if(!rt){ //找到对应位置就新建节点
          rt = ++tot;
    	  tr[rt].size = tr[rt].num = 1;
	  tr[rt].val = val;
          tr[rt].rd = rand();
	  return;
      }

      tr[rt].size++; //因为插入了数,所以在路径上每个节点的size都会加1
      if(tr[rt].val == val){
	  tr[rt].num++;
	  return;
      } //找到了直接返回

      int d = val > tr[rt].val ? 1 : 0;
      Insert(tr[rt].ch[d], val); //否则递归查找插入位置

      if(tr[rt].rd > tr[tr[rt].ch[d]].rd)
	  Rotate(rt, d ^ 1); //小修一波 
		
      Pushup(rt);
  }
   void Delete(int &rt, int val){
		if(!rt) return;
		if(val < tr[rt].val)
			Delete(lson(rt), val);
		else if(val > tr[rt].val)
			Delete(rson(rt), val);
		else{
			if(!lson(rt) && !rson(rt)){
				tr[rt].num--;
				tr[rt].size--;
				if(!tr[rt].num) rt = 0;
			}
			else if(lson(rt) && !rson(rt)){
				Rotate(rt, 1);
				Delete(rson(rt), val);
			}
			else if(!lson(rt) && rson(rt)){
				Rotate(rt, 0);
				Delete(lson(rt), val);
			}
			else if(lson(rt) && rson(rt)){
				int d = tr[lson(rt)].rd > tr[rson(rt)].rd ? 1 : 0;
				Rotate(rt, d);
				Delete(tr[rt].ch[d], val);
			}
		}
		
		Pushup(rt);
	} //大修大改 
  int Rank(int rt, int val){
      if(!rt) return 0;
      if(tr[rt].val == val)
 	  return tr[lson(rt)].size + 1; //找到了就返回最小的那个
      if(tr[rt].val > val)
	  return Rank(lson(rt), val); //如果查找的数在x的左边,则直接往左边查
      else
	  return Rank(rson(rt), val) + tr[lson(rt)].size + tr[rt].num; //否则往右边查,左边的所有数累加进答案
  }
  int Find(int rt, int pos){
      if(!rt) return 0;
      if(tr[lson(rt)].size >= pos)
	  return Find(lson(rt), pos);
      else if(tr[lson(rt)].size + tr[rt].num < pos)
	  return Find(rson(rt), pos - tr[rt].num - tr[lson(rt)].size);
      else
	  return tr[rt].val;
  }
   int Pre(int rt, int val){
       if(!rt) return -INF; //防止越界,同时-INF无法更新答案
       if(tr[rt].val >= val)
    //如果该节点的权值大于等于要找的权值
    //则不能成为前驱,递归查找左子树(有可能找到前驱)
	   return Pre(lson(rt), val);
       else //找右子树中是否存在前驱
	   return max(Pre(rson(rt), val), tr[rt].val);
   }

   int Suf(int rt, int val){ //同上
       if(!rt) return INF;
       if(tr[rt].val <= val)
	   return Suf(rson(rt), val);
       else
	   return min(Suf(lson(rt), val), tr[rt].val);
   }

完整Code:

例题:P3369 【模板】普通平衡树

#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define lson(x) tr[x].ch[0]
#define rson(x) tr[x].ch[1]

using namespace std;

const int MAXN = 1e5 + 10;
const int INF = 1e9;
int n, a, root, ans;

inline int read(){
	int x = 0, f = 1;
	char c = getchar();

	while(c < '0' || c > '9'){
		if(c == '-') f = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9'){
		x = (x << 1) + (x << 3) + (c ^ 48);
		c = getchar();
	}

	return x * f;
}

struct Treap{
	int tot;
	struct Tree{
		int rd;
		int val;
		int num;
		int size;
		int ch[2];

		Tree(){
			rd = val = num = size = 0;
			ch[0] = ch[1] = 0;
		}
	}tr[MAXN];

	Treap(){
		tot = 0;
	}

	void Pushup(int rt){
		tr[rt].size = tr[lson(rt)].size + tr[rson(rt)].size + tr[rt].num;
	}

	void Rotate(int &rt, int d){
		int son = tr[rt].ch[d ^ 1];

		tr[rt].ch[d ^ 1] = tr[son].ch[d];
		tr[son].ch[d] = rt;

		Pushup(rt);
		Pushup(son);

		rt = son;
	} //之前这玩意简直不是人写的 

	void Insert(int &rt, int val){
		if(!rt){
			rt = ++tot;
			tr[rt].size = tr[rt].num = 1;
			tr[rt].val = val;
			tr[rt].rd = rand();
			return;
		}

		tr[rt].size++;
		if(tr[rt].val == val){
			tr[rt].num++;
			return;
		}

		int d = val > tr[rt].val ? 1 : 0;
		Insert(tr[rt].ch[d], val);

		if(tr[rt].rd > tr[tr[rt].ch[d]].rd)
			Rotate(rt, d ^ 1);
			
		Pushup(rt);
	}

	void Delete(int &rt, int val){
		if(!rt) return;
		if(val < tr[rt].val)
			Delete(lson(rt), val);
		else if(val > tr[rt].val)
			Delete(rson(rt), val);
		else{
			if(!lson(rt) && !rson(rt)){
				tr[rt].num--;
				tr[rt].size--;
				if(!tr[rt].num) rt = 0;
			}
			else if(lson(rt) && !rson(rt)){
				Rotate(rt, 1);
				Delete(rson(rt), val);
			}
			else if(!lson(rt) && rson(rt)){
				Rotate(rt, 0);
				Delete(lson(rt), val);
			}
			else if(lson(rt) && rson(rt)){
				int d = tr[lson(rt)].rd > tr[rson(rt)].rd ? 1 : 0;
				Rotate(rt, d);
				Delete(tr[rt].ch[d], val);
			}
		}
		
		Pushup(rt);
	}

	int Rank(int rt, int val){
	    if(!rt) return 0;
	    if(tr[rt].val == val)
			return tr[lson(rt)].size + 1;
	    if(tr[rt].val > val)
			return Rank(lson(rt), val);
		else
	    	return Rank(rson(rt), val) + tr[lson(rt)].size + tr[rt].num;
	}

	int Find(int rt, int pos){
		if(!rt) return 0;
		if(tr[lson(rt)].size >= pos)
			return Find(lson(rt), pos);
		else if(tr[lson(rt)].size + tr[rt].num < pos)
			return Find(rson(rt), pos - tr[rt].num - tr[lson(rt)].size);
		else
			return tr[rt].val;
	}

	int Pre(int rt, int val){
		if(!rt) return -INF;
		if(tr[rt].val >= val)
			return Pre(lson(rt), val);
		else
			return max(Pre(rson(rt), val), tr[rt].val);
	}

	int Suf(int rt, int val){
		if(!rt) return INF;
		if(tr[rt].val <= val)
			return Suf(rson(rt), val);
		else
			return min(Suf(lson(rt), val), tr[rt].val);
	}
}T;


int main(){
    srand(time(0));
	n = read();
	for(register int i = 1; i <= n; i++){
		int opt, x;
		opt = read(), x = read();
		
		if(opt == 1) T.Insert(root, x);
		else if(opt == 2) T.Delete(root, x);
		else if(opt == 3) printf("%d\n", T.Rank(root, x));
		else if(opt == 4) printf("%d\n", T.Find(root, x));
		else if(opt == 5) printf("%d\n", T.Pre(root, x));
		else if(opt == 6) printf("%d\n", T.Suf(root, x));
	}

	return 0;
}

标签:rt,val,int,tr,Treap,有旋,lson,节点,模板
来源: https://www.cnblogs.com/TSTYFST/p/16486209.html