zoukankan      html  css  js  c++  java
  • 平衡树

    平衡树学习笔记

    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

  • 相关阅读:
    datanode报错Problem connecting to server
    使用命令查看hdfs的状态
    Access denied for user root. Superuser privilege is requ
    ElasticSearch默认的分页参数 size
    SparkStreaming Kafka 维护offset
    【容错篇】Spark Streaming的还原药水——Checkpoint
    251 Android 线性与相对布局简介
    250 Android Studio使用指南 总结
    249 如何解决项目导入产生的中文乱码问题
    248 gradle更新问题
  • 原文地址:https://www.cnblogs.com/Vanyun/p/13283397.html
Copyright © 2011-2022 走看看