【13】红黑树、并查集

1. 红黑树
	1.1 定义
		(1) 结点是红色或黑色。
		(2) 根结点是黑色。
		(3) 所有叶子都是黑色。(叶子是NIL结点)
		(4) 每个红色结点的两个子结点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色结点)
		(5) 从任一节结点其每个叶子的所有路径都包含相同数目的黑色结点。
	1.2 性质
		从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。
	1.3 平衡操作
		1.3.1 插入
			1.3.1.1 被插入的节点是根节点。
				直接把此节点涂为黑色。
			1.3.1.2 被插入的节点的父节点是黑色。
				什么也不需要做。
			1.3.1.3 被插入的节点的父节点是红色。
				[1] 当前节点的祖父节点的另一个子节点(叔叔节点)也是红色。
					(1) 将“父节点”设为黑色。
					(2) 将“叔叔节点”设为黑色。
					(3) 将“祖父节点”设为“红色”。
					(4) 将“祖父节点”设为“当前节点”(红色节点);即,之后继续对“当前节点”进行操作。
				[2] 叔叔节点是黑色,且当前节点是其父节点的右孩子
					(1) 将“父节点”作为“新的当前节点”。
					(2) 以“新的当前节点”为支点进行左旋。
				[3] 叔叔节点是黑色,且当前节点是其父节点的左孩子
					(1) 将“父节点”设为“黑色”。
					(2) 将“祖父节点”设为“红色”。
					(3) 以“祖父节点”为支点进行右旋。
		1.3.2 删除
			1.3.2.1 x指向一个"红+黑"节点。
				将x设为一个"黑"节点即可。
			1.3.2.2 x指向根。
				将x设为一个"黑"节点即可。
			1.3.2.3 
				[1] x的兄弟节点是红色。
					(1) 将x的兄弟节点设为“黑色”。
					(2) 将x的父节点设为“红色”。
					(3) 对x的父节点进行左旋。
					(4) 左旋后,重新设置x的兄弟节点。
				[2] x的兄弟节点是黑色,x的兄弟节点的两个孩子都是黑色。
					(1) 将x的兄弟节点设为“红色”。
					(2) 设置“x的父节点”为“新的x节点”。
				[3] x的兄弟节点是黑色;x的兄弟节点的左孩子是红色,右孩子是黑色的。
					(1) 将x兄弟节点的左孩子设为“黑色”。
					(2) 将x兄弟节点设为“红色”。
					(3) 对x的兄弟节点进行右旋。
					(4) 右旋后,重新设置x的兄弟节点。
				[4] x的兄弟节点是黑色;x的兄弟节点的右孩子是红色的,x的兄弟节点的左孩子任意颜色。
					(1) 将x父节点颜色 赋值给 x的兄弟节点。
					(2) 将x父节点设为“黑色”。
					(3) 将x兄弟节点的右子节设为“黑色”。
					(4) 对x的父节点进行左旋。
			
					
2. 并查集
	2.1 定义
		用森林维护集合关系,支持合并、查询等操作。
	2.2 优化
		(1) 路径压缩
		(2) 按秩合并

题目描述

一共有 n n n 个数,编号是 1 到 n 1 到n 1n,最开始每个数各自在一个集合中。

现在要进行 m m m 个操作,操作共有两种:

  1. M a b,将编号为 a a a b b b 的两个数所在的集合合并,如果两个数已经在同一个集合中,则忽略这个操作;
  2. Q a b,询问编号为 a a a b b b 的两个数是否在同一个集合中;
输入格式

第一行输入整数 n n n m m m

接下来 m m m 行,每行包含一个操作指令,指令为 M a bQ a b 中的一种。

输出格式

对于每个询问指令 Q a b,都要输出一个结果,如果 a a a b b b 在同一集合内,则输出 Yes,否则输出 No

每个结果占一行。

数据范围

1 < = n , m < = 1 0 5 1<= n,m <= 10^5 1<=n,m<=105

输入样例:
4 5
M 1 2
M 3 4
Q 1 2
Q 1 3
Q 3 4
输出样例:
Yes
No
Yes

算法

C++ 代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;

const int N = 100010;

int n, m;
int p[N], r[N];

int find(int x){
	//路径压缩 
	if(p[x] != x) p[x] = find(p[x]);
	return p[x];
}
void merge(int a, int b){
	a = find(a), b = find(b);
	//按秩合并 
	if(a == b) return;
	if(r[a] > r[b]) p[b] = a;
	else{
		p[a] = b;
		if(r[a] == r[b]) r[b] ++;
	}
}
int main(){
	scanf("%d%d", &n, &m);
	
	for(int i = 1; i <= n; i++){
		p[i] = i;
		r[i] = i;
	}
	while(m --){
		char op[2];
		int a, b;
		scanf("%s%d%d", op, &a, &b);
		if(*op == 'Q'){
			if(find(a) == find(b)) puts("Yes");
			else puts("No");
		}
		else merge(a, b);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值