今天AC了三个有关二叉树的题目:
P1827 [USACO3.4] 美国血统 American Heritage_lxh0113的博客-CSDN博客
https://blog.csdn.net/lxh0113/article/details/128522831?spm=1001.2014.3001.5502
P1030 [NOIP2001 普及组] 求先序排列_lxh0113的博客-CSDN博客
然后学习了并查集的相关内容:
并查集
并查集是什么,简而言之,就是——有关集合的合并,查找(找自己是属于哪一个集合)。
并查集,就我目前接触的而言,我是在kruskal算法中看到了是比较重要的。
并查集有几个重要的函数:(下面所说的“爹”指的是所属集合的代号)
- 找“爹”
- 合并“爹”
首先我们要做的是初始化,刚开始大家都是自己属于自己的那个小小集合的,所以我们需要把他们赋值。我们通常是不从0开始(那样子处理起来会有点麻烦,都一样,但是我们习惯从1开始数集合)
代码如下:
找爹,就是找到自己所属的集合。这边我使用的是靠左原则,什么叫靠左原则,就是说如果遇到俩个集合是不同的“爹”,他们需要合并,那么我们默认把右边的归顺到左边。也就是改右边的“爹”到左边的,以左边先出现为大。
为什么要找“爹”呢,因为这是我们判断他们是否在同一个集合的标准,如果他们在同一个集合在实际应用中会使顶点成环。这是我们所不希望看见的。“爹”是告诉我们他们同属的集合。
代码非常简单
但是这短短的几行代码,可是做了不少事情。getfather语句第二行if(a[x]==x) return x;我们知道靠左原则,也就是说右边始终会归顺左边,而左边是爹,左边的值不会变的,所以我们知道当我们遇到a[x]==x就是说等于它本身的时候,那么这个就是最高等级的爹。而如果不是,我们需要执行下面else后的语句。k=getfather(a[x]);return a[k];这俩句话,第一句话很明显是找到最终的爹,为什么说是最终的,因为我们可能会出现下面这种情况:
上面的圆形代表是几号元素,下面代表他们的爹。现在我们要合并2号和3号。1号2号元素已经在1号的集合里面了,而3号元素的爹是它自己,我们就需要找到2号元素最终的爹,因为2号元素他的爹不是自己我们要代入下一次找得下标就是a[1],就是2号下面所显示的元素。所以就有了递归,把2号当前的值改成最终的(因为我们在合并的时候,很可能要历经好几次,我们在找的时候最好改一下最终“爹”,保证下次递归会减少次数),而下面的return a[k]语句就是会返回最终答案。
合并爹,其实就是合并集合拉啦,这个也是几行代码:
这个代码的核心思想是,找到x,y元素对应的最终“爹”。然后 把y所对应的最终爹 改成 x所对应的最终爹。
因为只要改了爹,然后最终的爹会到一个集合当中。
最后还有一个很重要的代码部分,就是我们需要知道这里面有几个集合,聪明的你肯定想到了,只要数出当前元素和其值是否相等即可。代码如下:
#include<stdio.h>
#define N 100
int a[N],m,n,b[N][2];
int getfather(int x)
{
int k;
if(a[x]==x) return x;
else
{
k=getfather(a[x]);
return a[k];
}
}
int mergefather(int x,int y)
{
int p,q;
p=getfather(x);
q=getfather(y);
if(p!=q)
{
a[q]=p;
}
}
int main()
{
int i,count=0;
puts("输入你所要的总数:");
scanf("%d",&n);
for(i=1;i<=n;i++)
a[i]=i;
puts("输入总的线索:");
scanf("%d",&m);
puts("输入谁和谁是一起的:");
for(i=0;i<m;i++)
{
scanf("%d%d",&b[i][0],&b[i][1]);
}
for(i=0;i<m;i++)
{
mergefather(b[i][0],b[i][1]);
}
for(i=1;i<=n;i++)
{
if(a[i]==i) count++;
}
printf("%d ",count);
return 0;
}