学弟讲算法-图论-并查集与最小生成树

一、目的

给定一个无向图,求最小生成树

二、环境

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;
}

运行结果

猜你喜欢

转载自blog.csdn.net/qq_41452937/article/details/106877269
今日推荐