首页 > 其他分享 >旋转Treap树

旋转Treap树

时间:2023-07-06 16:23:46浏览次数:38  
标签:return rs int 旋转 Treap ls 节点 size

#include<bits/stdc++.h>
using namespace std;

const int M=1e6+10;
int cnt = 0; // cnt记录当前节点个数,最新一个节点即t[cnt]
int root = 0; // root是整棵树的树根,初始为空树所以root初始化=0
int n; // n表示操作次数

struct Node{
	int ls, rs; // 左右儿子
	int val, pri; // val:权值;pri:随机的优先级
	int size; // 当前结点为根的子树的结点数量,用于求第k大和rank
}t[M]; // tree[],存树

void newNode(int x){ // 初始化一个新结点
    // 新节点初始是叶子结点,之后在插入节点函数中调整位置
	cnt++; // 从t[1]开始存储结点,0表示空结点(若子节点为0即无子节点)
	t[cnt].size = 1; // 最初子树大小为1(即叶子结点自身)
	t[cnt].ls = t[cnt].rs = 0; // 叶子结点没有子结点
	t[cnt].val = x; // val: 赋予指定的键值
	t[cnt].pri = rand(); // pri:随机产生一个优先级,rand()是随机数函数
}

void update(int u){ // 更新以u为根的子树的size
	t[u].size = t[t[u].ls].size + t[t[u].rs].size+1;
}

void rotate(int &a, bool d){ // 当前位置的节点,当前操作
    // 具体地,d=true则左旋,d=false则右旋
    // a表示当前位置的节点:在旋转中,当前位置的节点会变
    // int& a表示a为一个int值的地址
    // 简单来说,即使得修改a的同时也修改函数外的值
    // 使用返回值可以达到同样的效果
    int b; // 节点B,同时是新插入的节点
    if(d == true){ // 左旋
        b = t[a].rs; // B是A的右儿子(right son)
        t[a].rs = t[b].ls;
        // 这里是解决儿子被顶替的情况,即上文的节点C
        t[b].ls = a; // A变成B的左儿子(left son)
    }
    else{ // 右旋,操作差不多,方向反过来
        b = t[a].ls; // B是A的左儿子
        t[a].ls = t[b].rs;
        t[b].rs = a; // A变成B的右儿子
    }
    t[b].size = t[a].size; // 继承子树大小
    update(a); // 重新计算A点的size,此处略
    a = b; // 此处a不再代表A,而代表子树的“根”
    // B节点代替A成为子树的“根”
} 

void insert(int &u, int x){ // 当前位置的节点 插入的权值
    // u和旋转部分函数的a同理
	if(u == 0){ // 假如找到了空位(空叶子节点)
        newNode(x); // 初始化权值为x的新节点,此处略
        u = cnt; // cnt是节点个数,同时也是新节点的编号
        return; // 直接返回
    }
    // 则新节点在当前位置的子树中
	t[u].size++; // 子树大小累加
    // 注:val是权值,pri是优先级
	if(x >= t[u].val) //如果新权值大于当前点
        insert(t[u].rs,x); //递归右子树找空叶子,直到插入
	else // 如果新权值小于当前点
        insert(t[u].ls,x); //同上,递归左子树
	if(t[u].ls!=0 && t[u].pri>t[t[u].ls].pri)
        rotate(u, 0); // 判断是否需要旋转
	if(t[u].rs!=0 && t[u].pri>t[t[u].rs].pri)
        rotate(u, 1);
	update(u); // 重新计算当前位置的size,此处略
}

void del(int &u, int x){ // 当前遍历到的节点 要删除的节点
    // u和旋转部分函数的a同理
    // 遍历到u意味着:要删除的节点必然在u的子树中
	t[u].size--; // 删除目标节点后u子树中少了一个节点
	if(t[u].val == x){ // 如果当前的节点就是要删除的节点
		if(t[u].ls == 0 && t[u].rs == 0){ // 如果u无依无靠、孤苦伶仃
            u = 0; // 直接把它删除掉
            return; // 返回
        } // 注意一下,这些if中只要返回了就不会进行之后的判断和计算
        // 相当于if else if结构
		if(t[u].ls == 0 || t[u].rs == 0){ // 如果u只有一个儿子
            u = t[u].ls + t[u].rs; // 把u修改成儿子,相当于移动到儿子节点上
            // 这一步是为了函数中最后一行,更新儿子的size值
            // 可能用返回值会更好理解?
            return;
        }
		if(t[t[u].ls].pri < t[t[u].rs].pri){
			rotate(u, 0);
			del(t[u].rs, x);
			return;
		}
		else{
			rotate(u, 1);
			del(t[u].ls, x);
			return;
		}
	}
	if(t[u].val >= x) // 经典的二分查找部分,略
        del(t[u].ls,x); // 往左子树找
	else del(t[u].rs,x); // 在右子树找
    // 此时必然已经删除完毕,"u"是被删节点的儿子
	update(u); // 更新size值
}

int Rank(int u, int x){ // 排名(从小到大排序,x为第几个)
	if(u == 0) return 0;
	if(x > t[u].val)
        return t[t[u].ls].size + 1 + Rank(t[u].rs, x);
	return Rank(t[u].ls, x);
}

int kth(int u,int k){ // 求第k大数
    // 注意:事实上,函数的k是表示求“以u为根的子树中的第k大数”
    // k会在函数传递的过程中改变(这是计划的一部分)
	if(k == t[t[u].ls].size + 1) // 如果这个数就是子树中第k大的数
        return t[u].val; // u刚好就是要查找的数
	else // 如果不是要找的数,就继续二分查找
        if(k > t[t[u].ls].size+1) // 如果第k大在右子树里
            return kth(t[u].rs, k-t[t[u].ls].size-1);
    		// 右子树(为了排除左子树中较小的数,要修改k的值)
		else return kth(t[u].ls, k); // 左子树(因为左子树就是u子树里最小的一部分,所以不用修改k)
}

int precursor(int u, int x){ // 求x的前驱(略)
	if(u == 0) return 0;
	if(t[u].val >= x)
        return precursor(t[u].ls, x);
	int tmp = precursor(t[u].rs,x);
	if(tmp == 0) return t[u].val;
	return tmp;
}

int successor(int u,int x){ // 求x的后继(略)
	if(u == 0) return 0;
	if(t[u].val <= x) return successor(t[u].rs,x);
	int tmp = successor(t[u].ls,x);
	if(tmp == 0) return t[u].val;
	return tmp;
}

signed main(){
	scanf("%d",&n); // 输入n
	while(n--){ // 循环n次
        int opt, x; // opt表示操作类型,x为对应的值
        scanf("%d%d", &opt, &x);
        switch (opt){ // 避免大量if的出现
            case 1: insert(root, x); break; // 插入新节点
            case 2: del(root,x); break; // 删除节点
            case 3: printf("%d\n", Rank(root, x)+1); break; // 输出排名
            case 4: printf("%d\n", kth(root, x)); break; // 输出第x大数
            case 5: printf("%d\n", precursor(root, x)); break; // 输出x前驱
            case 6: printf("%d\n", successor(root, x)); break; // 输出x后继
        }
	}
	return 0;
}

标签:return,rs,int,旋转,Treap,ls,节点,size
From: https://www.cnblogs.com/meteor2008/p/17532495.html

相关文章

  • 坚固型3DMAG™A31315LOLATR-XZ-S-AR-10、A31315LOLATR-XY-S-SE-10霍尔效应 线性,旋转位
    A313153D磁性位置传感器IC是完全集成的坚固型3DMAG™霍尔效应磁性位置传感器IC,主要用于支持汽车、工业和消费类应用中的各种非接触式旋转和线性位置测量。此传感器IC符合AEC-Q1000级的要求,并根据ISO26262进行开发。这些标准使A31315成为了要求严格的汽车安全系统的理想选择,适......
  • 怎么旋转图片?在线旋转图片(批量)
    功能地址在线图片旋转,翻转照片,设置照片旋转角度,批量免费|TOFORU在线工具软件定制地址:https://tool.toforu.com/f/img_roate.html功能说明在线图片旋转,翻转照片,设置照片旋转角度,批量免费。支持参数:旋转角度功能使用相关知识图片旋转是一种常见的图像处理操作,它可......
  • Java使用joml计算机图形学库,将3D坐标旋转正交投影转为2D坐标
    最近遇到了一个困扰我许久的难题,现将解决方案分享出来由于我们的项目侧重点在前端绘图,导致了前后端工作量不协调,我后端接口很快就能写完,而前端一个图要画好久,领导见状将前端的任务分到后端一部分用Java代码来实现,然后给前端提供接口而我接到的任务就是将Echarts中绘制三维图形的......
  • C#图片旋转
    这里以Bitmap为例说明问题。可以看到,旋转方法需要传入一个参数,而这个参数是一个枚举类型,RotateFlipType。系统提供了两大类型的旋转,1.旋转后不翻转。2.旋转后接着翻转。翻转的轴可以为X和Y,对应为水平和垂直。经测试,它们的这样分的,如图所示,3代表水平的轴,2代表垂直的轴。 ......
  • 189. 旋转数组
    给定一个整数数组nums,将数组中的元素向右轮转k个位置,其中k是非负数。示例1:输入:nums=[1,2,3,4,5,6,7],k=3输出:[5,6,7,1,2,3,4]解释:向右轮转1步:[7,1,2,3,4,5,6]向右轮转2步:[6,7,1,2,3,4,5]向右轮转3步:[5,6,7,1,2,3,4]本题思路一致https://......
  • leetcode 48 旋转图像 rotate-image【ct】
    ====思路:1.对角线翻折  i=0;i<matrix.lengthj=i;j<matrix.lengthmatrix[i][j]matrix[j][i]=matrix[j][i]matrix[i][j]2.左右翻折i=0i<matrix.lengthj=0j<Math.floor(matrix.length/2)matrix[i][j]matrix[i][matrix.lengt......
  • 旋转高频电压注入PMSM无感控制MATLAB仿真模型,Mat
    旋转高频电压注入PMSM无感控制MATLAB仿真模型,MatID:2238606091675051......
  • 简单旋转
    @OverridepublicvoidonDraw(Canvascanvas){//Thissavesoffthematrixthatthecanvasappliestodraws,soitcanberestoredlater.canvas.save();//nowwechangethematrix//Weneedtorotatearoundthecenterofourtext//......
  • vue学习第16天 CSS---3D转换 (translate3d 3d移动、3D旋转 rotate3d、transform-
    3D转换转换:1)3d移动 translate3d 2)3d旋转 rotate3d 3D的特点:1)近大远小2)物体后面遮挡不可见 3D转换:我们工作最常用的 3D位移 和 3D旋转 主要知识点: 1、三维坐标系(z轴,z外(屏幕)+,z内(屏幕)-)三维......
  • 旋转数组
    题:给定一个数组,将数组中的元素向右移动k个位置,其中k是非负数。1.示例1:输入:[1,2,3,4,5,6,7]和k=3输出:[5,6,7,1,2,3,4]解释:向右旋转1步:[7,1,2,3,4,5,6]向右旋转2步:[6,7,1,2,3,4,5]向右旋转3步:[5,6,7,1,2,3,4]2.算法思路:一般人拿到这道题大概会使用......