并查集可用于求图中连通分量的数量,利用一个pre数组,记录节点的上级是谁,形成树状结构,每一棵树构成一个连通分量
在Kruskal算法中,需要判断当前待加入的边是否属于同一个连通分量,故需要利用并查集
【题目1】
LeetCode 547. Friend Circles
领扣 547. 朋友圈
求图中连通分量的数量
class Solution {
public:
int findCircleNum(vector<vector<int>>& M) {
int n = M.size();
vector<int> pre( n, 0 );
// 初始化pre数组,每个节点的pre设置为自己
for( int i = 0; i < n; i++ )
pre[i] = i;
// 遍历所有edge,将该edge的两个节点合并
for( int i = 0; i < n; i++ )
for( int j = i + 1; j < n; j++ )
if( M[i][j] )
merge( i, j, pre );
// 统计连通分量数量
int result = 0;
for( int i = 0; i < n; i++ )
if( pre[i] == i )
result++;
return result;
}
int find_captain( int x, vector<int>& pre )
{
int cur = x;
while( pre[cur] != cur )
cur = pre[cur];
// 路径压缩:把沿路的节点的pre直接指向cur
int temp = x;
while( temp != cur )
{
pre[temp] = cur;
temp = pre[temp];
}
return cur;
}
void merge( int x, int y, vector<int>& pre )
{
int c1 = find_captain( x, pre );
int c2 = find_captain( y, pre );
if( c1 != c2 )
pre[c1] = c2;
}
};
【题目2】poj 1287 Networking
Kruskal算法求图的最小生成树
第1行为节点个数n
和边数e
,节点编号为1
到n
接下来e
行,分别表示相互连接的两个节点以及边的权重
当n
为0
时,输入结束
#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>
#define MAXN 50
using namespace std;
class Edge
{
public:
int p1, p2, cost;
Edge( int p1, int p2, int cost )
{
this->p1 = p1;
this->p2 = p2;
this->cost = cost;
}
};
bool cmp( Edge a, Edge b )
{
return a.cost < b.cost; // 升序
}
int n, e;
int pre[MAXN + 10];
int p1, p2, cost;
// 查询x所在队伍的队长
int find_captain( int x )
{
int cur = x;
while( pre[cur] != cur )
cur = pre[cur]; // cur为x的队长
// 路径优化:把x到cur的沿路每个节点的pre直接设置为cur
int temp = x;
while( temp != cur )
{
pre[temp] = cur;
temp = pre[temp];
}
return cur;
}
// 合并x和y所在的队伍
void merge( int x, int y )
{
int c1 = find_captain(x);
int c2 = find_captain(y);
if( c1 != c2 )
pre[c1] = c2; // 指定新队伍的队长
}
int Kruskal( vector<Edge>& vec )
{
// 对所有边从小到大排序
sort( vec.begin(), vec.end(), cmp );
// 每个节点自成一个小队
for( int i = 0; i < n; i++ )
pre[i] = i;
int result = 0;
for( int i = 0; i < vec.size(); i++ )
// 如果节点p1和p2属于不同的“队伍”(p1和p2属于不同的连通分量)
// 则合并p1和p2的队伍,选择该条边
if( find_captain( vec[i].p1 ) != find_captain( vec[i].p2 ) )
{
merge( vec[i].p1, vec[i].p2 );
result += vec[i].cost;
}
return result;
}
int main()
{
while( scanf( "%d", &n ) == 1 && n )
{
scanf( "%d", &e );
vector<Edge> vec;
for( int i = 0; i < e; i++ )
{
scanf( "%d%d%d", &p1, &p2, &cost );
vec.push_back( Edge( p1 - 1, p2 - 1, cost ) ); // 规定节点编号从0开始
}
printf( "%d\n", Kruskal( vec ) );
}
return 0;
}