并查集

第一次接触到并查集是我学习Kruskal算法的时候。因为要求解最小生成树,所以开始的需要把每个点都看成单独的个体,之后把有联系且距离最小的点不断并入集合,过程中不可出现回路。这个过程我们就是通过并查集来解决的。Kruskal算法我先按下不表,先来仔细回顾一下并查集。

并查集一般用于求解几个个体之间的关系,并查集也可以说是说是把相同属性的个体放入一个集合的同时为几种不同属性或关系的个体划清界限的方法。并查集得到的关系集合其实也是一棵关系数。下面来说说实现的过程:三个函数 ①initSet() ②findRoot() ③union()

上面可能说的很抽象,这里我们举几个例子:①我们给出一个家庭关系图,即给出两个节点就说明他们两个存在血缘关系,我们把之间有血缘关系的人分组的过程就叫并查集。②在一群人里,我们给出他们的敌友关系,之后为他们分出阵营,并判断是否为敌对阵营的过程也是用并查集。还有很多相关的例子我一下子也说不了很多,但都与这些类似。(要有什么新颖问题请在评论为我指出,在此感谢!!)

①作用是初始化集合,表现在把父子关系的数组全部初始化一下。

int parent[105];
void initSet(int n){
    for(int i=1;i<=n;i++){
        parent[i] = -1;
    }
}

②作用是寻找一个点的根节点是谁,并查集算法的核心就在于找出要合并的点的根与已经在集合中的那点的根看看是不是相同,若相同的话就不合并(会出现回路),否则就把点并入集合。

int findRoot(int x){
    int s;
        //寻找根节点
    for(s=x;parent[s]>=0;s=parent[s]);//这里根节点的parent[]值是-1
        //压缩路径
    while(s!=x){
        int temp = parent[x];
        parent[x] = s;
        x = temp;
    }
    return s;
}

根节点的parent[]为何取值-1在此不表,自行思考。后文会有所交代   O(∩_∩)O哈哈~

这里说说路径的压缩:

为什么要压缩路径?因为出现退化树是很苦恼的一件事!!就如下图这棵树只有一个叶子节点的情况。

对于一般情况,得到的树是左图的样子,在做了一次压缩路径后就变成了右图的样子

接下来再说一下③作用就是合并,对就是调用②求出根节点并作出判断后来执行合并操作。

void Union(int R1,int R2){
    int r1 = findRoot(R1),r2 = findRoot(R2);
    int temp = parent[r1] + parent[r2];
    if(r1<r2){
        parent[R2] = r1;
        parent[r1] = temp;
    }else{
        parent[R1] = r2;
        parent[r2] = temp;
    }
}

看完代码,我立刻意思到之前根节点的parent[]赋值为-1的作用,它可以用来记录每一个集合包含了几个点,这种方法也算是一种优化,使得计数的过程更加直观,但有一点需要注意,parent[]统计的数量是负数!!

这里再来说说压缩路径,通过观察代码会发现每一次合并就回执行一次压缩路径。

因此可以看出通过压缩路径算法在寻根的是时候有了很大优化。

但有的题目有一些特殊要求,因此调用压缩路径时也要因题而异。

 

posted @ 2018-03-03 21:27  javier_macro  阅读(146)  评论(0)    收藏  举报