一、目的
给定一个无向图,求最小生成树
二、环境
Gcc 4.9.2
三、过程
3.1 并查集
数据定义
// x的父亲是y
p[x] = y;
//初始化
void init(int i){
//初始状态时i的父亲就是它本身
p[i] = i;
}
查询
//查询i的节点的源头
int find(int i){
int b = i;
//当i的父亲不是它本身时,继续查找
while(i != p[i]){
i = p[i]
}
//路径压缩,方便下次查询
while(b != i){
//将查询路径上所有节点的父亲设为i
p[b] = i;
b = p[b];
}
}
路径压缩图示:
合并
//合并节点,将a节点的父亲设为b节点
void unions(int a , int b){
int x = find(a);
int y = find(b);
if(x != y ){
p[x] = y;
}
}
统计集合数量
//n为节点数
int count(){
int sum = 0 ;
for(int i = 0 ; i< n ;i++){
if( i == p[i] ){
sum++;
}
}
return sum;
}
3.2 最小生成树
使用kruskal法求最小生成树
数据定义
const int MAXZ = 10005;
//边的定义
typedef struct E {
//起点
int u;
//终点
int v;
//权值
int w;
E(int s, int e, int w) : u(s), v(e), w(w) {}
} E;
//边的集合
vector<E> edge;
克鲁斯卡尔法求最小生成树
int cmp(E& e1, E& e2) {
return e1.w <= e2.w;
}
int kruskal() {
//将所有边按权值从小到大排序
sort(edge.begin(), edge.end(), cmp);
//记录最小生成树权值
int sum = 0;
for (int i = 0; i < edge.size(); i++) {
int u = edge[i].u;
int v = edge[i].v;
//若该边两点不在同一集合内,说明不构成闭环,可以选择
if (find(u) != find(v)) {
sum += edge[i].w;
//将两点合并为同一个集合
unions(u, v);
}
}
return sum;
}
3.3 测试代码
#include <iostream>
#include <vector>
#include<algorithm>
using namespace std;
const int MAXZ = 10005;
typedef struct E {
int u; int v; int w;
E() {}
E(int s,int e,int w):u(s),v(e),w(w) {}
} E;
int p[MAXZ];
int a[6] = {0,1,2,3,4,5};
int cmp(E& e1, E& e2) {
return e1.w <= e2.w;
}
vector<E> edge;
//记录所选边的信息
vector<E> info;
void init() {
E e[10];
e[0] = E(1,6,1); e[1] = E(1,5,16);
e[2] = E(5,6,33); e[3] = E(6,2,11);
e[4] = E(6,4,14); e[5] = E(2,4,5);
e[6] = E(1,2,17); e[7] = E(5,4,4);
e[8] = E(2,3,6); e[9] = E(3,4,10);
for(int i = 0 ; i<10; i++) {
edge.push_back(e[i]);
}
}
int find(int i) {
int b = i;
while(i!=p[i]) {
i = p[i];
}
while(b!=i) {
p[b] = i;
b = p[b];
}
return i;
}
void unions(int a,int b) {
int x = find(a);
int y = find(b);
if(x!=y) {
p[x] = y;
}
}
int kruskal() {
init();
for(int i = 0 ; i<6; i++) {
p[i] = i;
}
sort(edge.begin(),edge.end(),cmp);
//记录最小生成树权值
int sum = 0;
for (int i = 0; i < edge.size(); i++) {
int u = edge[i].u;
int v = edge[i].v;
if (find(u) != find(v)) {
sum += edge[i].w;
unions(u,v);
//记录选择的边
info.push_back(edge[i]);
}
}
return sum;
}
int main(void) {
cout<<"最小生成树权值为:"<<kruskal()<<endl;
for(int i = 0 ; i< info.size(); i++) {
cout<<"选取边:"<<info[i].u<<"----"<<info[i].v<<" "<<"权值为:"<<info[i].w<<endl;
}
return 0;
}
运行结果