数据结构(C实现)------- 最小生成树之Kruskal算法

[本文是自己学习所做笔记,欢迎转载,但请注明出处:http://blog.csdn.net/jesson20121020]

算法描述:

Kruskal算法是按权值递增的次序来构造最小生成树的方法。

   假设G(V,E)最一个具有n个顶点的连通网,顶点集V={v1,v2,....,vn}。设所求的最小生成树为T={U,TE},其中U是T的顶点集,TE是T的边集,U和TE的初始值为空集。Kruskal算法的基本思想如下:将最小生成树初始化为T=(V,TE),仅包含
G的全部顶点,不包含G的任一条边,此时,T由n 个连通分量组成,每个分量只有一个顶点;将图中G中的边按权值从小到大排序,选择代价最小了一条边,若该边所依附的顶点分属于T中的不同的连通分量,则将此边加入到TE中,否则,舍弃此边而选择下一条代价最小的边。依次类推,直至TE中包含n-1条边(即T中所有的顶点都在同一连通分量上)为止。

算法实现:

   设置一个edge数组存储连通网中所有的边,为了便于选择当前权值最小的边,需要将edge中的边按权值从小到大进行排列。

   而在连通分量的合并上,可以采用集合的合并方法,对于有n个顶点的连通网,设置一个数组father[0...n-1],其初始值为-1,表示n个顶点在不同的连通分量上。然后,依次扫描edge数组中的每一条边,并查找相关联的两个顶点所属的连通分量,假设vf1和vf2为两个顶点的所在树的根节点的序号,若vf1不等于vf2,则表明这条边的两个顶点不属于同一个连通分量,则将这条边作为最小生成树的边并输出,然后合并它们所属的两个连通分量。

算法代码:

int findFather(int father[],int v){
	int t = v;
	while(father[t] != -1)
		t = father[t];
	return t;
}

/* *
 *Kruskal算法求最小生成树
 * */
void Kruskal_MG(MGraph MG,Edge edge[]){
	int father[MAX_VEX_NUM];
	int i,count,vf1,vf2;
	// 初始化father数组
	for(i = 0;i < MAX_VEX_NUM;i++){
		father[i] = -1;
	}
	i = 0;
	count = 0; // 统计加入最小生树中的边数
	// 遍历任意两个结点之间的边
	while(i < MG.arcnum && count < MG.arcnum){
		vf1 = findFather(father,edge[i].start);
		vf2 = findFather(father,edge[i].end);
		// 如果这两个节点不属于同一个连通分量,则加入同一个连通分量
		if (vf1 != vf2){
			father[vf2] = vf1;
			count++;
			printf("%c,%c,%d\n",MG.vexs[edge[i].start],MG.vexs[edge[i].end],edge[i].cost);
		}
		i++;
	}
}

   其中,函数findFather的作用就是找指定节点所属的连通分量,在这里也就是找其所在树的根节点在数组中的序号。

算法说明:

   对于带权图G中e条边的权值的排序方法可以有多种,这里采用的是最简单的冒泡排序法,时间复杂度为O(n^2)。而判断新选择的边的两个顶点是否在同一个连通分量中,这个问题等价于一个在最多有n 个顶点的生成树中遍历寻找新选择的边的两个节点是否存在的问题,所以此算法的复杂度最坏情况下是O(n^2)。

   注意:一般来讲,Prim算法的时间复杂度为O(n^2),因此适合于稠密图,而Kruskal算法需要对e条边进行排序,最快的情况下复杂度为O(elog2e),因此对于稀疏图,采用Kruskal算法比较合适。

完整代码:

/*
 * =====================================================================================
 *
 *       Filename:  Kruskal.c
 *
 *    Description:  最小生成树之Kruskal算法
 *
 *        Version:  1.0
 *        Created:  2015年05月06日 21时25分12秒
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  jesson20121020 (), [email protected]
 *   Organization:
 *
 * =====================================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#define MAX_VEX_NUM 50
#define MAX_ARC_NUM 100
#define UN_REACH 1000

typedef char VertexType;
typedef enum {
	DG, UDG
} GraphType;
typedef struct {
	VertexType vexs[MAX_VEX_NUM];
	int arcs[MAX_VEX_NUM][MAX_VEX_NUM];
	int vexnum, arcnum;
	GraphType type;
} MGraph;

/**
 * 根据名称得到指定顶点在顶点集合中的下标
 * vex  顶点
 * return 如果找到,则返回下标,否则,返回0
 */
int getIndexOfVexs(char vex, MGraph *MG) {
	int i;
	for (i = 1; i <= MG->vexnum; i++) {
		if (MG->vexs[i] == vex) {
			return i;
		}
	}
	return 0;
}

/**
 * 创建邻接矩阵
 */
void create_MG(MGraph *MG) {
	int i, j, k,weight;
	int v1, v2, type;
	char c1, c2;
	printf("Please input graph type DG(0) or UDG(1) :");
	scanf("%d", &type);
	if (type == 0)
		MG->type = DG;
	else if (type == 1)
		MG->type = UDG;
	else {
		printf("Please input correct graph type DG(0) or UDG(1)!");
		return;
	}

	printf("Please input vexmun : ");
	scanf("%d", &MG->vexnum);
	printf("Please input arcnum : ");
	scanf("%d", &MG->arcnum);
	getchar();
	for (i = 1; i <= MG->vexnum; i++) {
		printf("Please input %dth vex(char):", i);
		scanf("%c", &MG->vexs[i]);
		getchar();
	}

	//初始化邻接矩阵
	for (i = 1; i <= MG->vexnum; i++) {
		for (j = 1; j <= MG->vexnum; j++) {
			if(i == j)
				MG->arcs[i][j] = 0;
			else
				MG->arcs[i][j] = UN_REACH;
		}
	}

	//输入边的信息,建立邻接矩阵
	for (k = 1; k <= MG->arcnum; k++) {
		printf("Please input %dth arc v1(char) v2(char) weight(int): ", k);

		scanf("%c %c %d", &c1, &c2,&weight);
		v1 = getIndexOfVexs(c1, MG);
		v2 = getIndexOfVexs(c2, MG);
		if (MG->type == 1)
			MG->arcs[v1][v2] = MG->arcs[v2][v1] = weight;
		else
			MG->arcs[v1][v2] = weight;
		getchar();
	}

}
/**
 * 打印邻接矩阵和顶点信息
 */
void print_MG(MGraph MG) {
	int i, j;
	if(MG.type == DG){
		printf("Graph type: Direct graph\n");
	}
	else{
		printf("Graph type: Undirect graph\n");
	}

	printf("Graph vertex number: %d\n",MG.vexnum);
	printf("Graph arc number: %d\n",MG.arcnum);

	printf("Vertex set:\n ");
	for (i = 1; i <= MG.vexnum; i++)
		printf("%c\t", MG.vexs[i]);
	printf("\nAdjacency Matrix:\n");

	for (i = 1; i <= MG.vexnum; i++) {
		j = 1;
		for (; j < MG.vexnum; j++) {
			printf("%d\t", MG.arcs[i][j]);
		}
		printf("%d\n", MG.arcs[i][j]);
	}
}

// 定义边结构体
typedef struct{
	int start;
	int end;
	int cost;
}Edge;

/* *
 * 由邻接矩阵得到边的信息
 *
 * */
void init_edge(MGraph MG,Edge edge[]){
	int i,j;
	int count = 0;
	if(MG.type == 0){
		for(i = 1; i <= MG.vexnum;i++){
			for (j = 1;j <= MG.vexnum;j++){
				if(MG.arcs[i][j] != 0 && MG.arcs[i][j] != UN_REACH){
					edge[count].start = i;
					edge[count].end = j;
					edge[count].cost = MG.arcs[i][j];
					count++;
				}
			}
		}
	}
	else{
		for(i = 1; i <= MG.vexnum;i++){
			for (j = i;j <= MG.vexnum;j++){
				if(MG.arcs[i][j] != 0 && MG.arcs[i][j] != UN_REACH){
					edge[count].start = i;
					edge[count].end = j;
					edge[count].cost = MG.arcs[i][j];
					count++;
				}
			}
		}
	}

}

/* *
 * 将边按权值从大到小排序
 * */
void sort_edge(Edge edge[],int arcnum){
	int i,j;
	Edge temp;
	for(i = 0; i < arcnum - 1;i++){
		for (j = i+1;j < arcnum;j++){
			if(edge[i].cost > edge[j].cost){
				temp = edge[i];
				edge[i] = edge[j];
				edge[j] = temp;
			}
		}
	}
}

/* *
 * 输出边的信息
 * */
void print_edge(Edge edge[],int arcnum){
	int i = 0;
	while(i < arcnum){
		printf("%d,%d,%d\n",edge[i].start,edge[i].end,edge[i].cost);
		i++;
	}
}
/**
*    找出指定节点的所属的连通分量,这里是找出其根节点在father数组中下标。
**/ int findFather(int father[],int v){
	int t = v;
	while(father[t] != -1)
		t = father[t];
	return t;
}

/* *
 *Kruskal算法求最小生成树
 * */
void Kruskal_MG(MGraph MG,Edge edge[]){
	int father[MAX_VEX_NUM];
	int i,count,vf1,vf2;
	// 初始化father数组
	for(i = 0;i < MAX_VEX_NUM;i++){
		father[i] = -1;
	}
	i = 0;
	count = 0; // 统计加入最小生树中的边数
	// 遍历任意两个结点之间的边
	while(i < MG.arcnum && count < MG.arcnum){
		vf1 = findFather(father,edge[i].start);
		vf2 = findFather(father,edge[i].end);
		// 如果这两个节点不属于同一个连通分量,则加入同一个连通分量
		if (vf1 != vf2){
			father[vf2] = vf1;
			count++;
			printf("%c,%c,%d\n",MG.vexs[edge[i].start],MG.vexs[edge[i].end],edge[i].cost);
		}
		i++;
	}
}
/**
 * 主函数
 */
int main(void) {
	MGraph MG;
	Edge edge[MAX_ARC_NUM];
	create_MG(&MG);
	print_MG(MG);
	init_edge(MG,edge);
	sort_edge(edge,MG.arcnum);
	printf("the result of Kruskal:\n");
	Kruskal_MG(MG,edge);

	return EXIT_SUCCESS;
}

运行演示:

[email protected]:~/develop/worksapce/c_learning/最小生成树$ gcc -o Kruskal Kruskal.c
[email protected]:~/develop/worksapce/c_learning/最小生成树$ ./Kruskal
Please input graph type DG(0) or UDG(1) :0
Please input vexmun : 4
Please input arcnum : 5
Please input 1th vex(char):a
Please input 2th vex(char):b
Please input 3th vex(char):c
Please input 4th vex(char):d
Please input 1th arc v1(char) v2(char) weight(int): a b 1
Please input 2th arc v1(char) v2(char) weight(int): a c 3
Please input 3th arc v1(char) v2(char) weight(int): a d 4
Please input 4th arc v1(char) v2(char) weight(int): b c 2
Please input 5th arc v1(char) v2(char) weight(int): c d 3
Graph type: Direct graph
Graph vertex number: 4
Graph arc number: 5
Vertex set:
 a	b	c	d
Adjacency Matrix:
0	1	3	4
1000	0	2	1000
1000	1000	0	3
1000	1000	1000	0
the result of Kruskal:
a,b,1
b,c,2
c,d,3
时间: 2024-10-10 05:51:05

数据结构(C实现)------- 最小生成树之Kruskal算法的相关文章

最小生成树的Kruskal算法实现

最近在复习数据结构,所以想起了之前做的一个最小生成树算法.用Kruskal算法实现的,结合堆排序可以复习回顾数据结构.现在写出来与大家分享. 最小生成树算法思想:书上说的是在一给定的无向图G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边(即),而 w(u, v) 代表此边的权重,若存在 T 为 E 的子集(即)且为无循环图,使得的 w(T) 最小,则此 T 为 G 的最小生成树.说白了其实就是在含有 n 个顶点的连通网中选择 n-1 条边,构成一棵极小连通子图,并使该连

最小生成树之Kruskal算法

上一篇文章中提到了最小生成树的Prim算法,这一节继续探讨一下最小生成树的Kruskal算法.什么是最小生成树算法上文已经交代过了,所以我们直接从Kruskal的步骤开始介绍. 1.Kruskal算法的步骤: a.假定拓扑图的边的集合是E,初始化最小生成树边集合G={}. b. 遍历集合E中的所有元素,并且按照权值的大小进行排序. c. 找出E中权值最小的边e . d .如果边e不和最小生成树集合G中的边构成环路,则将边e加到边集合G中:否则测试下一条权值次小的边,直到满足条件为止. e. 重复

poj——2031 最小生成树(MST) Kruskal算法

poj——2031 最小生成树(MST)  Kruskal算法 Building a Space Station Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 4985   Accepted: 2503 Description You are a member of the space station engineering team, and are assigned a task in the constructio

HDU1875——畅通工程再续(最小生成树:Kruskal算法)

畅通工程再续 Description相信大家都听说一个“百岛湖”的地方吧,百岛湖的居民生活在不同的小岛中,当他们想去其他的小岛时都要通过划小船来实现.现在政府决定大力发展百岛湖,发展首先要解决的问题当然是交通问题,政府决定实现百岛湖的全畅通!经过考察小组RPRush对百岛湖的情况充分了解后,决定在符合条件的小岛间建上桥,所谓符合条件,就是2个小岛之间的距离不能小于10米,也不能大于1000米.当然,为了节省资金,只要求实现任意2个小岛之间有路通即可.其中桥的价格为 100元/米. Input输入

(最小生成树)Kruskal算法

(最小生成树)Kruskal算法 算法要点:Kruskal算法的最难点在于怎样判断加入边(x, y)后是否形成了环. 问题可化简为:判断边(x, y)的两个顶点在图(实际是森林)mst中是否已连通.如果已经连通,加入边将形成环: 否则,不形成环. Kruskal算法中, 要用到并查集的合并与查找. 完整代码如下:(具体见代码注释) 1 #include<bits/stdc++.h> 2 using namespace std; 3 int fa[1000010]; 4 int n, m; 5

算法导论——最小生成树:Kruskal算法(利用了并查集)

package org.loda.graph; import org.loda.structure.MinQ; import org.loda.structure.Queue; import org.loda.util.In; /** * * @ClassName: KruskalMST * @Description:Kruskal最小生成树算法 * @author minjun * @date 2015年5月25日 下午10:50:01 * */ public class KruskalMST

图的最小生成树:Kruskal算法实现

图的最小生成树,就是基于图,假设其有n的顶点,那么就要构建一颗连通树,使其各边权重和最小.最小生成树的实现算法主要有两种:Prim算法和Kruskal算法.Prim算法在前面已经介绍过,本文着重介绍Kruskal算法及其实现,其中图的实现以及相关操作,采用前面博文C++ 图的实现中的实现方式,由于本文重点在于Kruskal算法的实现,所有就不在图的构建以及相关操作中过多赘述. 对于Kruskal算法,维基的解释其实已经很详细了,算法思想很好理解,不多说明,直接看实现. /* *无向图查找最小树:

邻接矩阵c源码(构造邻接矩阵,深度优先遍历,广度优先遍历,最小生成树prim,kruskal算法)

matrix.c #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <limits.h> #include "aqueue.h" #define MAX_VALUE INT_MAX #define MAX_NUM 100 typedef char node_type; typedef struct matrix { node_type vertex[M

最小生成树(Kruskal算法)

最小生成树就是一张图能生成的边权最小的树. 方法(Kruskal算法):将所有边权从小到大排序,然后一条一条边检查,如果加入这条边形成了回路,那么不加入树中,否则加入.至于如何判断回路,用并查集维护即可. 代码: 1 #include<cstdio> 2 #include<cstdlib> 3 #include<cmath> 4 #include<cstring> 5 #include<iostream> 6 #include<algori