(基础)--- 并查集、堆

并查集

  • 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("");
	}
}
posted @ 2020-08-14 18:37  chstor  阅读(169)  评论(0编辑  收藏  举报