• 平衡树


    平衡树学习笔记

    FHQ Treap

    前置芝士

    BST的性质:
    根节点左子树的值均小于等于根节点,右子树的值均大于根节点 
    

    例题

    我们需要支持以下操作

    split

    inline void split(int now, int &x, int &y, int k){ //裂开 
    	if(!now) return (void)(x = y = 0); //如果没有根即没有树可裂,x,y均为0 
    	if(val[now] <= k){
    		x = now; //如果根的值小于等于k则k要大于now左子树所有的值,所以把左子树裂开,让右子树接着裂 
    		split(ch[now][1], ch[x][1], y, k);
    	}else{
    		y = now; //相反 
    		split(ch[now][0], x, ch[y][0], k);
    	}
    	up(now);
    	//裂开以后所有比k小的值均在x树上,比k大的值均在y树上 
    } 
    

    merge

    inline void merge(int &now, int x, int y){
    	if(!siz[x] or !siz[y]) return (void)(now = siz[x] ? x : y); //x 和 y 有一个空树的话直接返回不空树 
    	if(key[x] < key[y]){ //当x的键值小于y的键值 ,让y和x的右子树并 
    		now = x;
    		merge(ch[now][1], ch[x][1], y); 
    		up(now);
    	}else{
    		now = y;//相反 
    		merge(ch[now][0], x, ch[y][0]);
    		up(now);
    	}
    } 
    

    delete

    如何删除一个数num,很巧妙的方法

    1.我们把root树以num为关键字裂开,此时x树上的值均小于等于num

    2.我们再去裂x树,此时以num-1为关键字,则裂开的两个树中有一个树上的值均等于 num(我们设这颗树为z)

    3.直接合并z的左右子树,此时已经把z的根节点移除

    4.合并

    inline void del(int num){ 
    	split(root, x, y, num);
    	split(x, x, z, num-1);
    	merge(z, ch[z][0], ch[z][1]);
    	merge(x, x, z);
    	merge(root, x, y);
    }
    

    insert

    很简单

    inline void ins(int num){
    	split(root, x, y, num);
    	merge(x, x, new_node(num));
    	merge(root, x, y);
    }
    

    kth

    寻找第k排名的数

    (暂时还不透彻,会补上注释的....汗)

    inline int kth(int now, int k){ 
    	while(1){
    		if(k <= siz[ch[now][0]]){
    			now = ch[now][0];
    		}else{
    			if(k == siz[ch[now][0]] + 1) return now;
    			else{
    				k -= siz[ch[now][0]] + 1;
    				now = ch[now][1];
    			}
    		}
    	}
    }
    

    rnk

    num的排名就是所有小于num的数的数量+1,此时已经显然

    inline int rnk(int num){
    	split(root, x, y, num-1);
    	int res = siz[x] + 1;
    	merge(root, x, y);
    	return res;
    }
    

    pre

    前驱怎么找?

    前驱是不是所有小于num的值里最大的值!

    我们以num-1为关键字去把root树裂开,此时x树上的值是所有小于num的数,

    我们再找x树里面排名为size[x]的数,是不是就是所有小于num的数里面最大的数?即前驱。

    inline int pre(int num){ //找前驱 
    	split(root, x, y, num-1);
    	int res = val[kth(x,siz[x])];
    	merge(root, x, y);
    	return res;
    }
    

    nxt

    后继怎么找...

    后继是不是所有大于num的值里最小的值.....

    我们以num为关键字去把root树裂开,此时y树上的值是所有大于num的数,

    我们再找y树里面排名为1的数,是不是就是所有大于num的数里面最小的数?即后继。

    inline int nxt(int num){
    	split(root, x, y, num);
    	int res = val[kth(y,1)];
    	merge(root, x, y);
    	return res;
    }
    

    总代码看不看无所谓的吧,反正就是并在一起了....

    code

    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn = 1e5 + 10;
    
    struct Treap{
    	int ch[maxn][2], val[maxn], key[maxn], siz[maxn];
    	
    	int sum, root, x, y, z, n; //sum记录节点编号 
    	
    	inline void up(int now){ //维护一下大小 
    		siz[now] = siz[ch[now][0]] + siz[ch[now][1]] + 1; //它的大小就等于左右儿子大小的和+自己这个点 
    	}
    	inline int new_node(int num){ //新建一个点 
    		siz[++sum] = 1; //新建一个树(点),但大小为1 
    		val[sum] = num; //值为num 
    		key[sum] = rand(); //随机 
    		return sum; //返回节点编号 
    	}
    	inline void split(int now, int &x, int &y, int k){ 
    		if(!now) return (void)(x = y = 0);
    		if(val[now] <= k){
    			x = now;
    			split(ch[now][1], ch[x][1], y, k);
    		}else{
    			y = now;
    			split(ch[now][0], x, ch[y][0], k);
    		}
    		up(now);
    	} 
    	inline void merge(int &now, int x, int y){ 
    		if(!siz[x] or !siz[y]) return (void)(now = siz[x] ? x : y);
    		if(key[x] < key[y]){
    			now = x;
    			merge(ch[now][1], ch[x][1], y); 
    			up(now);
    		}else{
    			now = y;
    			merge(ch[now][0], x, ch[y][0]);
    			up(now);
    		}
    	} 
    	inline void del(int num){ 
    		split(root, x, y, num);
    		split(x, x, z, num-1);
    		merge(z, ch[z][0], ch[z][1]);
    		merge(x, x, z);
    		merge(root, x, y);
    	}
    	inline void ins(int num){
    		split(root, x, y, num);
    		merge(x, x, new_node(num));
    		merge(root, x, y);
    	}
    	inline int kth(int now, int k){
    		while(1){
    			if(k <= siz[ch[now][0]]){
    				now = ch[now][0];
    			}else{
    				if(k == siz[ch[now][0]] + 1) return now;
    				else{
    					k -= siz[ch[now][0]] + 1;
    					now = ch[now][1];
    				}
    			}
    		}
    	}
    	inline int rnk(int num){
    		split(root, x, y, num-1);
    		int res = siz[x] + 1;
    		merge(root, x, y);
    		return res;
    	}
    	inline int pre(int num){
    		split(root, x, y, num-1);
    		int res = val[kth(x,siz[x])];
    		merge(root, x, y);
    		return res;
    	}
    	inline int nxt(int num){
    		split(root, x, y, num);
    		int res = val[kth(y,1)];
    		merge(root, x, y);
    		return res;
    	}
    	void main(){
    		root = 0, x = y = z = 0;
    		scanf("%d", &n);
    		for(int i = 1, q, w; i <= n; i ++){
    			scanf("%d%d", &q, &w);
    			if(q == 1) ins(w);
    			if(q == 2) del(w);
    			if(q == 3) printf("%d
    ", rnk(w));
    			if(q == 4) printf("%d
    ", val[kth(root,w)]);
    			if(q == 5) printf("%d
    ", pre(w));
    			if(q == 6) printf("%d
    ", nxt(w));
    		}
    	}
    }FHQ;
    
    signed main(){
    	FHQ.main();
    	return 0;
    } 
    

    Splay

  • 相关阅读:
    shell--练习--简易计算器
    shell--运算符
    shell--传递参数
    PHP数学函数的练习
    PDO对数据库的操作
    PHP实现手机短信的验证
    ThinkPHP框架 _ 学习16
    ThinkPHP框架 _ 学习15
    ThinkPHP框架 _ 学习14
    ThinkPHP框架 _ 学习13
  • 原文地址:https://www.cnblogs.com/Vanyun/p/13283397.html
Copyright © 2020-2023  润新知