zoukankan      html  css  js  c++  java
  • treap详解

    一、二叉排序树
    因为只是来讲treap的,所以关于二叉排序树的知识就不再赘述。
    如果还不知道二叉排序树,可以先到别处学学再来看。
    在二叉排序树中,我们将比该节点小的值放在该节点的左边,将比该节点大的值放在该节点的右边。
    可是很显然,这样的话操作的时间复杂度就和树的深度有很大的关系。当树的形态为一条链的时候,就无法满足操作的复杂度为(nlog(n))
     
     
    二、treap是什么
    treap = tree + heap
    treap就是树与堆的结合体。为了防止树被单调的数据卡成一条链,可以给每个节点多加上一个随机的优先值,让树同时也满足堆的性质(一般为小根堆)。
    由于优先值是随机的,所以树的深度平均为(nlog(n))
     
     
    三、treap实现
    1、左旋(zig)与右旋(zag)
    我们发现,在一个节点的优先值被随机生成后,树可能无法满足我们所要求的堆的性质。
    所以为了让树同时也具备这个性质,需要对树进行一定的旋转,使树变成我们所希望的样子。
     
    左旋:将根节点的右儿子作为根,根节点作为右儿子的左儿子。
    右旋:将根节点的左儿子作为根,根节点作为左儿子的右儿子。

    由图中可知左旋与右旋的操作方法。

    void up(int k) { //更新子树的大小 
        size[k] = size[lson[k]] + size[rson[k]] + c[k];
    }
    
    void zig(int &k) { //左旋 
        int v = rson[k]; rson[k] = lson[v]; lson[v] = k;
    	size[v] = size[k]; up(k); k = v;
    }
    
    void zag(int &k) { //右旋 
    	int v = lson[k]; lson[k] = rson[v]; rson[v] = k;
    	size[v] = size[k]; up(k); k = v;
    }
    

     
     
    2、插入操作
    我们考虑在树上插入一个节点。
    先像二叉排序树一样找到应当插入节点的位置,插入该节点,并初始化该节点的值。
    由于树要满足小根堆的性质,所以如果插入的节点的优先级小于它的根节点,就通过左旋/右旋进行调整。(不理解的可以看看上面那张图)

    void insert(int &k, int x) { //插入节点 
    	if (k == 0) { //如果没有该节点,直接插入
    	  k = ++ cnt;  
    	  size[k] = c[k] = 1; a[k] = x; pri[k] = rand();
    	  return;
    	}
    	size[k] ++;
    	if (a[k] == x) c[k] ++;
    	else if (x > a[k]) {
    	  insert(rson[k], x);
    	  if (pri[rson[k]] < pri[k]) zig(k);
    	}
    	else {
    	  insert(lson[k], x);
    	  if (pri[lson[k]] < pri[k]) zag(k);
    	}
    }
    

     
     
    3、删除操作
    考虑在树上删除一个节点。
    首先先在树上找到该点。
    对于这个点,有两种情况:要么它有一个或没有儿子,要么它有两个儿子。
    先考虑只有一个或没有儿子的情况,显然这个点可以直接删除,将它的子树接到它的父节点上即可。
    对于有两个儿子的情况,显然是不能直接删除这个点的。(因为无法确定左右子所应接在父节点上的位置)
    所以对于有两个儿子的情况,可以通过左旋/右旋将待删除的点向叶子节点处旋转,直到只有一个或没有儿子为止。
    在旋转时,如果改点的左儿子的优先级比右儿子小,就右旋;如果大,就左旋。

    void del(int &k, int x) { //删除 
    	if (k == 0) return; //没有该节点就直接退出 
    	if (a[k] == x) {
    	  if (c[k] > 1) c[k] --, size[k] --;
    	  else {
    	  	if (!lson[k] || !rson[k]) k = lson[k] + rson[k];
    	  	else if (pri[lson[k]] < pri[rson[k]]) zag(k), del(k, x);
    	  	else zig(k), del(k, x);
    	  }
    	}
    	else if (x > a[k]) size[k] --, del(rson[k], x);
    	else size[k] --, del(lson[k], x);
    }
    

     
     
    4、查询一个数的排名
    查询排名,就是查询该数是这些数中第几小的。
    查询排名的方法和二叉排序树一样,这里就不再说了。

    int query_rank(int k, int x) { //查询排名 
    	if (k == 0) return 2e9;
    	if (a[k] == x) return size[lson[k]] + 1;
    	if (x > a[k]) return size[lson[k]] + c[k] + query_rank(rson[k], x);
    	else return query_rank(lson[k], x);
    }
    

     
     
    5、查询排名第x的数是什么
    即是询问这些数中从小到大排后第x大的数是什么。
    查询方法也和二叉排序树一样,直接放代码。

    int query_num(int k, int x) { //查询排名为x的数 
    	if (k == 0) return 2e9;
    	if (x <= size[lson[k]]) return query_num(lson[k], x);
    	x -= size[lson[k]];
    	if (x <= c[k]) return a[k];
    	x -= c[k];
    	return query_num(rson[k], x);
    }
    

     
     
    6、查询数x的前驱
    即查询比x小的最大的数。
    和二叉排序树一样。如果x比该数大,就往右走;否则往左走。

    int query_pre(int k, int x) { //查询前驱 
    	if (k == 0) return -2e9;
    	if (a[k] < x) return max(a[k], query_pre(rson[k], x));
    	else return query_pre(lson[k], x);
    }
    

     
     
    7、查询数x的后继
    即查询比x大的最小的数。
    也和二叉排序树一样。如果x比该数小,就往左走;否则往右走。

    int query_next(int k, int x) { //查询后继 
    	if (k == 0) return 2e9;
    	if (a[k] > x) return min(a[k], query_next(lson[k], x));
    	else return query_next(rson[k], x);
    }
    

     
     
    四、treap模板
    放一题treap的模板题。
    bzoj3224

    代码如下:

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    
    const int maxn = 100005;
    int n, cnt, root;
    int lson[maxn], rson[maxn], size[maxn], a[maxn], c[maxn], pri[maxn];
    
    int read(void) {
        char c; while (c = getchar(), (c < '0' || c > '9') && c != '-'); int x = 0, y = 1;
    	if (c == '-') y = -1; else x = c - '0';
    	while (c = getchar(), c >= '0' && c <= '9') x = x * 10 + c - '0'; return x * y; 
    }
    
    int rand() { //生成随机数 
        static int seed = 2333;
        return seed = (int)((((seed ^ 998244353) + 19260817ll) * 19890604ll) % 1000000007);
    }
    
    void up(int k) { //更新子树的大小 
    	size[k] = size[lson[k]] + size[rson[k]] + c[k];
    }
    
    void zig(int &k) { //左旋 
    	int v = rson[k]; rson[k] = lson[v]; lson[v] = k;
    	size[v] = size[k]; up(k); k = v;
    }
    
    void zag(int &k) { //右旋 
    	int v = lson[k]; lson[k] = rson[v]; rson[v] = k;
    	size[v] = size[k]; up(k); k = v;
    }
    
    void insert(int &k, int x) { //插入节点 
    	if (k == 0) { //如果没有该节点,直接插入
    	  k = ++ cnt;  
    	  size[k] = c[k] = 1; a[k] = x; pri[k] = rand();
    	  return;
    	}
    	size[k] ++;
    	if (a[k] == x) c[k] ++;
    	else if (x > a[k]) {
    	  insert(rson[k], x);
    	  if (pri[rson[k]] < pri[k]) zig(k);
    	}
    	else {
    	  insert(lson[k], x);
    	  if (pri[lson[k]] < pri[k]) zag(k);
    	}
    }
    
    void del(int &k, int x) { //删除 
    	if (k == 0) return; //没有该节点就直接退出 
    	if (a[k] == x) {
    	  if (c[k] > 1) c[k] --, size[k] --;
    	  else {
    	  	if (!lson[k] || !rson[k]) k = lson[k] + rson[k];
    	  	else if (pri[lson[k]] < pri[rson[k]]) zag(k), del(k, x);
    	  	else zig(k), del(k, x);
    	  }
    	}
    	else if (x > a[k]) size[k] --, del(rson[k], x);
    	else size[k] --, del(lson[k], x);
    }
    
    int query_rank(int k, int x) { //查询排名 
    	if (k == 0) return 2e9;  
    	if (a[k] == x) return size[lson[k]] + 1;
    	if (x > a[k]) return size[lson[k]] + c[k] + query_rank(rson[k], x);
    	else return query_rank(lson[k], x);
    }
    
    int query_num(int k, int x) { //查询排名为x的数 
    	if (k == 0) return 2e9;
    	if (x <= size[lson[k]]) return query_num(lson[k], x);
    	x -= size[lson[k]];
    	if (x <= c[k]) return a[k];
    	x -= c[k];
    	return query_num(rson[k], x);
    }
    
    int query_pre(int k, int x) { //查询前驱 
    	if (k == 0) return -2e9;
    	if (a[k] < x) return max(a[k], query_pre(rson[k], x));
    	else return query_pre(lson[k], x);
    }
    
    int query_next(int k, int x) { //查询后继 
    	if (k == 0) return 2e9;
    	if (a[k] > x) return min(a[k], query_next(lson[k], x));
    	else return query_next(rson[k], x);
    }
    
    int main() {
    	n = read();
    	  while (n --) {
    	  	int opt = read(), x = read();
    	  	  if (opt == 1) insert(root, x);
    	  	  else if (opt == 2) del(root, x);
    	  	  else if (opt == 3) printf("%d
    ", query_rank(root, x));
    	  	  else if (opt == 4) printf("%d
    ", query_num(root, x));
    	  	  else if (opt == 5) printf("%d
    ", query_pre(root, x));
    	  	  else if (opt == 6) printf("%d
    ", query_next(root, x));
    	  }
    	return 0;
    } 
    
  • 相关阅读:
    spark的环境安装
    (7)zabbix资产清单inventory管理
    (6)zabbix主机与组配置
    (5)zabbix配置详解
    (4)zabbix监控第一台服务器
    (3)zabbix用户管理
    (2)zabbix硬件需求
    (1) zabbix进程构成
    centos7系统root无法通过su切换到某个普通用户
    01基础复习
  • 原文地址:https://www.cnblogs.com/Gaxc/p/10123505.html
Copyright © 2011-2022 走看看