项湫

导航

Kruskal算法生成最小生成树

1. 问题

最小生成树:一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边

2. 解析

 

在一给定的无向图G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边(即),而 w(u, v) 代表此边的权重,若存在 T E 的子集(即)且为无循环图,使得的 w(T) 最小,则此 T G 的最小生成树。

 

Kruskal算法:假设现在要求无向连通图G=(V, E)的最小生成树TKruskal算法的思想是令T的初始状态为|V|个结点而无边的非连通图,T中的每个顶点自成一个连通分量。接着,每次从图G中所有两个端点落在不同连通分量的边中,选取权重最小的那条,将该边加入T中,如此往复,直至T中所有顶点都在同一个连通分量上。

注意此处的关键点有两个:

1)在生成最小生成树前,要对图中的所有边进行排序;

2)如何判断一条边的两个端点是否落在不同的连通分量上

3. 设计

[核心伪代码]

4. 分析

由于各个子块不是嵌套而是顺序的,所以时间复杂度为O(ElogE)

5. 源码

#include<stdio.h>

#include<stdlib.h>

#include<iostream>

using namespace std;

 

#define MAXN 11 //顶点个数的最大值

#define MAXM 20 //边的个数的最大值

struct edge //边

{

int u, v, w;

}edges[MAXM]; //边的数组

int parent[MAXN]; //parent[i]为顶点i所在集合对应的树中的根结点

int n, m; //顶点个数、边的个数

int i, j; //循环变量

void UFset() //初始化

{

for (i = 1; i <= n; i++) parent[i] = -1;

}

int Find(int x) //查找并返回结点x所属集合的根结点

{

int s; //查找位置

for (s = x; parent[s] >= 0; s = parent[s]);

while (s != x) //优化方案——压缩路径,使后续的查找操作加速

{

int tmp = parent[x];

parent[x] = s;

x = tmp;

}

return s;

}

//运用并查集,将两个不同集合的元素进行合并,使两个集合中任意两个元素都连通

void Union(int R1, int R2)

{

int r1 = Find(R1), r2 = Find(R2); //r1和r2分别为R1和R2的根结点

int tmp = parent[r1] + parent[r2]; //两个集合结点数之和(负数)

//如果R2所在树结点个数 > R1所在树结点个数(注意parent[r1]是负数)

if (parent[r1] > parent[r2])

{

parent[r1] = r2;

parent[r2] = tmp;

}

else

{

parent[r2] = r1;

parent[r1] = tmp;

}

}

int cmp(const void* a, const void* b) //实现从小到大的比较函数

{

edge aa = *(const edge*)a, bb = *(const edge*)b;

return aa.w - bb.w;

}

void Kruskal()

{

int sumweight = 0; //生成树的权值

int num = 0; //已选用的边的数目

UFset(); //初始化parent数组

for (i = 0; i < m; i++)

{

if (Find(edges[i].u) != Find(edges[i].v))

{

printf("%d %d %d\n", edges[i].u, edges[i].v, edges[i].w);

sumweight += edges[i].w; num++;

Union(edges[i].u, edges[i].v);

}

if (num >= n - 1) break;

}

printf("The weight of MST is : %d\n", sumweight);

}

void main()

{

scanf("%d%d", &n, &m); //读入顶点个数和边数

for (int i = 0; i < m; i++)

scanf("%d%d%d", &edges[i].u, &edges[i].v, &edges[i].w); //读入边的起点和终点

printf("The edges chosen are :\n");

qsort(edges, m, sizeof(edges[0]), cmp); //对边按权值从小到大排序

Kruskal();

}

 

posted on 2021-03-08 23:04  项湫  阅读(192)  评论(0编辑  收藏  举报