tk's blog
  • tk's blog Read Me
  • 算法相关
    • 数据结构
      • 【集训整理】 旋转treap模板
      • 二叉树及相关数据结构的java语言实现
      • 快乐树0x01:AVL树的java实现
      • 快乐树0x02 线段树实现(c++)
      • 链表的Java语言实现
    • 算法
      • DP的背包问题小结-java语言描述
      • 【集训整理】2-SAT问题 模板题
      • 【集训整理】Tarjan算法 模板题
      • 【集训整理】差分约束 模板题
      • 【集训整理】最近公共祖先LCA 模板题
      • 二分查找与二分答案-java实现
      • 动态规划-java语言练习一:暴力DP
      • 快速幂
      • 状态压缩DP-java描述
      • 差分
      • 乘法逆元
    • 题解
      • CFRound-GoodBye2022题解
  • java相关
    • Java与算法竞赛——注意事项摘录
    • java面向对象简要总结 一
    • java面向对象简要总结 三
    • java面向对象简要总结 二
  • 后端相关
    • Linux-Crontab命令
    • Spring Data JPA 使用方法
    • Spring集成Artemis实现JSM的异步消息传递
    • Spring使用自定义配置项
    • MIT6.824分布式系统Lab1.MapReduce笔记
    • MIT6.824分布式系统Lab2-Raft-A笔记
    • MIT6.824分布式系统Lab2-Raft-B笔记
  • 杂谈
    • 杂谈-关于2021
  • 杂项
    • c语言 scanf的返回值
    • 系统设计
  • 计科基础
    • 编译原理
      • 编译原理:词法分析笔记
    • CSAPP 第二章笔记
    • 计算机组成原理笔记
    • CSAPP Lab1. Datalab
    • CSAPP Lab2 Bomblab
  • C++每日一题
    • C++每日一题 Day 1 肥宅水
    • C++每日一题 Day 2 数字反转
    • C++每日一题 Day 3 理五的凡尔赛风气
    • C++每日一题 Day 4 我喜欢这个数
    • C++每日一题 Day 5 数字楼梯
    • C++每日一题 Day 6 插火把
    • C++每日一题 Day 7 贪吃蛇
    • C++每日一题 Day 8 蒙德最强战力
    • C++每日一题 Day 9 璃月七星选举
    • C每日一题 Day 2 肥宅水
    • C每日一题 Day 3 理五的凡尔赛风气
    • C语言每日一题 Day 1 荧妹好感队
由 GitBook 提供支持
在本页
  1. 算法相关
  2. 数据结构

【集训整理】 旋转treap模板


Treap是一种弱平衡的二叉搜索树,它同时符合二叉搜索树和堆的性质:

二叉搜索树:右子节点值>父节点>左子节点

堆:子节点的优先值比父节点值小

我们让每个节点的值符合二叉搜索树的性质,同时给每个节点赋一个随机的优先值,让优先值满足堆的性质。

由于优先值是随机的,一旦不满足堆性质就会产生旋转,所以就很难出现退化成链的情况,达到平衡的效果。

Treap分为旋转Treap和无旋treap两种,旋转treap的旋转操作分为右旋和左旋:

![image-20220703180723552](【集训整理】 旋转treap模板.assets/image-20220703180723552.png)

写成代码如下:

void left_rotate(int& id) { //左旋
	int tmp = rchild[id];
	rchild[id] = lchild[tmp];
	lchild[tmp] = id;
	id = tmp;
	push_up(lchild[id]);
	push_up(id);
}

void right_rotate(int& id) { //右旋
	int tmp = lchild[id];
	lchild[id] = rchild[tmp];
	rchild[tmp] = id;
	id = tmp;
	push_up(rchild[id]);
	push_up(id);
}

旋转操作其实只涉及两个点,交换下孩子即可。

树的储存,使用数组模拟结构体:

int lchild[maxn];
int rchild[maxn];
int val[maxn],prio[maxn];
int siz[maxn], cnt[maxn];
int tot, root;

val存值,prio存节点的优先值,siz记录某节点的子节点的数量,cnt记录某节点的值出现的次数,用于处理重复元素的情况。

创建节点:

int create(int v) {
	//创建节点,并给予随机优先值
	tot++;
	val[tot] = v;
	prio[tot] = rand();
	siz[tot] = 1;
	cnt[tot] = 1;
	return tot;
}

用rand函数给prio赋随机值。

更新siz的push_up操作:

void push_up(int id) {
	//用于旋转后重新计算size
	siz[id] = siz[lchild[id]] + siz[rchild[id]] + cnt[id];
}

在旋转/插入后siz可能会变化,要push_up从下到上更新。

初始化:

void build() { //初始化,插入一个正无穷和一个负无穷
	root = create(-inf);
	rchild[root] = create(inf);
	push_up(root);
}

初始化要先往里边塞一个正无限和一个负无限节点。

插入:

在二叉查找树上找位置插入,找到树缺位或已有节点修改。每次插完都要检测符不符合堆的性质,如果不符合就用旋转的方式进行平衡。

注意,每层插入递归结束后都要进行一次push_up重新计算size值。

void insert(int& id, int v) {
	if (!id) { //找到树缺位(引用指向的变量(l/rchild)为0)
		id = create(v);
		return;
	}
	if (v == val[id]) cnt[id]++;
	else { //遍历查找树,知道找到他该在的位置
		if (v < val[id]) {
			insert(lchild[id], v);
			//每次插完都要检测符不符合堆的性质,不符合就平衡
			if (prio[id] < prio[lchild[id]]) right_rotate(id);
		}
		else {
			insert(rchild[id], v);
			if (prio[id] < prio[rchild[id]]) left_rotate(id);
		}
	}
	push_up(id); //插入后size会变,沿路更新
}
上一页数据结构下一页二叉树及相关数据结构的java语言实现

最后更新于2年前