1 并查集(Union/Find)
1.1 并查集(C语言)
①并查集是树的应用
②合并的过程就是“认爹”,要遵守“靠左”原则和“擒贼先擒王”原则
③在每次判断两个结点是否已经在同一棵树中的时候(一棵树就是一个集合),也必须要求其根源,必须找到树的根节点,并判断两个结点的祖宗是否是同一个根节点才行
#include<stdio.h>
int f[1001] = { 0 }, n, m, sum = 0;
//这里是初始化,非常重要,数组里面存的是自己数组下标的编号
void init()
{
int i;
for (i = 1; i <= n; i++)
f[i] = i;
return;
}
//这里是找树的祖先的递归函数,不停的找,直到找到树的祖先为止
//“擒贼先擒王”原则
int getf(int v)
{
if (f[v] == v)
return v;
else
{
//这里是压缩路径,每次在函数返回的时候,顺带把路上遇到的人的上级改为最后找到树的祖先的编号
f[v] = getf(f[v]);
return f[v];
}
}
//这里是合并两子集合的函数
void merge(int v, int u)
{
int t1, t2; //t1,t2分别为v和u的祖先
t1 = getf(v);
t2 = getf(u);
if (t1 != t2) //判断两个结点是否在同一个集合中,即是否为同一个祖先
{
f[t2] = t1; //“靠左”原则,左边变成右边的boss(上级)。即把右边的集合,作为左边集合的子集合
}
return;
}
int main()
{
int i, x, y;
scanf("%d %d", &n, &m);
init();
for (i = 1; i <= m; i++)
{
//开始合并
scanf("%d %d", &x, &y);
merge(x,y);
}
//最后扫描有多少独立的集合
for (i = 1; i <= n; i++)
{
if (f[i] == i)
sum++;
}
printf("%d\n", sum);
getchar();
getchar();
return 0;
}
2 优先队列(Priority Queue)
支持插入元素和寻找最大(小)值元素的数据结构被称为优先队列
1.1 堆(C语言)
①堆是树在优先队列中的应用
②本例为最大堆排序,最小堆详见排序算法
③若求一个数列中第K大(/小)的数,只需建立一个大小为K的最大(/小)堆,堆顶就是第K大(/小)的数。e.g.n个数取第k大。第一步:任取k数建最小堆。第二部:从k+1个数开始,与堆顶的树比较,小于堆顶的不要,大于堆顶的舍弃当前堆顶并将新数作为新堆顶,siftdown维护堆。第三部:同样方法处理第k+2~n个数。
#include<stdio.h>
int h[101]; //用来存放堆的数组
int n; //用来存储堆中元素的个数,也就是堆的大小
//交换函数,用来交换堆中的两个元素的值
void swap(int x,int y)
{
int t;
t = h[x];
h[x] = h[y];
h[y] = t;
return;
}
//向下调整函数
void siftdown(int i) //传入一个需要向下调整的节点编号i,这里传入1,即从对的顶点开始向下调整
{
int t, flag = 0; //flag用来标记是否需要继续向下调整
//当i结点有儿子(其实是至少有左儿子)并且有需要继续调整的时候循环就执行
while (i * 2 <= n && flag == 0)
{
//首先判断它和左儿子的关系,并用t纪录值较大的结点编号
if (h[i] < h[i * 2]) //#################变化处####################
t = i * 2;
else
t = i;
//如果它有右儿子,再对右儿子进行讨论
if (i * 2 + 1 <= n)
{
//如果右儿子的值更大,更新较大的结点编号
if (h[t] < h[i * 2 + 1]) //#################变化处####################
t = i * 2 + 1;
}
//如果发现最大的结点编号不是自己,说明子结点中有比父结点更大的
if (t != i)
{
swap(t, i); //交换它们,注意swap函数需要自己来写
i = t; //更新i为刚才与它交换的儿子结点的编号,便于接下来继续向下调整
}
else
flag = 1; //否则说明当前的父结点已经比两个子结点都要大了,不需要再进行调整了
}
return;
}
//建立堆的函数
void creat()
{
int i;
//从最后一个非叶结点到第1个结点依次进行向下调整
for (i = n / 2; i >= 1; i--)
{
siftdown(i);
}
return;
}
//#################变化处####################
//堆排序
void heapsort() //每次把最大的放数组最后
{
while(n > 1)
{
swap(1,n);
n--;
siftdown(1);
}
return;
}
int main()
{
int i, num;
//读入要排序的数字的个数
scanf("%d", &num);
for (i = 1; i <= num; i++)
scanf("%d", &h[i]);
n = num;
//建堆
creat();
//堆排序
heapsort();
//输出
for (i = 1; i <= num; i++)
printf("%d ", h[i]);
getchar();
getchar();
return 0;
}