(基础)--- 并查集、堆
并查集
- union:将两个集合合并
- query:询问两个元素是否在一个集合当中
基本原理:每个集合用一棵树来表示。树根的编号就是整个集合的编号。每个节点存储它的父节点,p[x]表示x的父节点
问题一:如何判断树根:if(p[x] == x)
问题二:如何求x的集合编号:while(p[x] != x) x = p[x];
优化:路径压缩
int find(int x) //返回x的祖宗节点+路径压缩
{
if(p[x] != x) p[x]=find(p[x]);
return p[x];
}
问题三:如何合并两个集合:px是x的集合编号,py是y集合编号。p[x]=y
p[find(a)] = find(b);//合并两个集合
注意:记得初始化数组
//初始化
void init()
{
for(int i=1;i<=n;i++) p[i]=i;
}
堆
- 1、插入一个数
heap[++size] = x;
up(size);
- 2、求集合当中的最小值
heap[1];
- 3、删除最小值
heap[1] = heap[size];
size--;
down(1);
- 4、删除任意一个元素
heap[k] = heap[size];
size--;
down(k);up(k);
- 5、修改任意一个元素
heap[k] = x;
down(k);up(k);
线性时间复杂度的算法将该堆调整为最小堆(小根堆)
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e3+10;
int n,m;
int heap[N],size;
void down(int u) {
int t=u;
if(u*2<=size&&heap[u*2]<heap[t]) t = u*2;
if(u*2+1 <= size && heap[u*2+1]<heap[t]) t=u*2+1;
if(t != u) {
swap(heap[t],heap[u]);
down(t);
}
}
int main() {
cin>>n>>m;
for(int i=1; i<=n; i++) cin>>heap[i];
size = n;
for(int i=n/2; i; i--) {
down(i);
}
}
逐个按顺序插入到初始为空的最小堆(小根堆)
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e3+10;
int n,m;
int heap[N],size;
void up(int u) {
while(u/2 && heap[u/2]>heap[u]) {
swap(heap[u/2],heap[u]);
u/=2;
}
}
int main() {
cin>>n>>m;
int x;
for(int i=1; i<=n; i++) cin>>x,heap[++size]=x,up(size);
//输出h[i]到根节点的路径
for(int i=0; i<m; i++) {
int j;
cin>>j;
cout<<heap[j];
while(j>1) {
j/=2;
cout<<" "<<heap[j];
}
puts("");
}
}