克鲁斯卡尔(kruskal)算法代码释义

算法解释:

克鲁斯卡尔(kruskal)算法,建立最小生成树的算法,观察这个图,
步骤是依次选择权值最小的边,构成最小生成树,注意是树,所以依次选择的各边不能构成环。

实现

在这里插入图片描述
针对这个图,我们定义一个集合IntEdges,用来存储未被添加到生成树中的边,并根据各边的按权值的大小进行排序,对这个集合进行排序,
但这里有一个问题,如何再添加一条边之后不让当前的变成呢。回想一下我们完过的贪吃蛇的游戏,如果因为吃一个果子,咬到了自己的身体,是不是就死掉了呢:
假如当前有一个生成树(暂且不要在乎权值大小,只是举个例子):顶点是
v7、v6、v0、v5(蓝色连线部分),这时候如果有边(v6,v5)添加进来,会构成闭环,所以不能添加进来,通过观察发现,条件总结起来,就一句话:
新添加的边的两个顶点,不能出现在同一个生成树中
在这里插入图片描述
那么如何判断两个顶点不出现在同一个生成树中呢,这时候我们就想到了树,**每个树都有一个树根节点,如果两个顶点有相同的树根节点,那么他们两个肯定是在同一个生成树中喽,**就像如果你能和某个人找到相同的祖宗,是不是就可以认为是一家人了呢(在同一颗树中)(对树不熟悉的同学可以复习一下树的知识),例如图中举例的生成树:
v7—>v6—>v1—>v5
接下来,就是选择一种数据存储结构,来表示这个树,目的是,每次输入一个顶点,就输出其树根节点,还记得静态链表吗,我们利用数组下标的特点,使用数组存储链式的树结构
定义一个数组 var parent []int,针对上面的例子,有:

parent[5]=1 //v5的双亲是v1
parent[1]=6  //v1的双亲是v6
parent[6]=7 //v6的双亲是v7

在设计一个函数,寻找树根节点,

func FindRoot(parent *[]int, f int) int {

	for (*parent)[f] != f {  
		f = (*parent)[f]
	}
	return f
}

大家对代码中的 *parent)[f] != f 可能有疑惑,没关系,我来解释:
既然我们定义了parent数组,但是如果不初始化,那么是没法使用的,但是如和初始化呢,看图,从下图中找出已经存在的生成树:
在这里插入图片描述
你看这图里面什么也没有,搞不好你说我耍你!别急,往下看:
在这里插入图片描述
图中的每个小自环都是一棵生成树,每棵树只有一个节点,而且还是树根节点,自己给自己当祖宗,parent[0]=0,parent[1]=1,parent[2]=2…
所以我们的初始化代码就来了:

for i := 0; i < G.NumVex; i++ {
		//自己是自己的前驱
		parent[i] = i
	}

Now,我们把树也讲清楚了,存储的数组也初始化了,都能找自己的祖宗了,下面就开始找生成树把,首先,不能忘了我们按权值大小排序好的边集,
他是长这样滴:
在这里插入图片描述
拿出最小的第一个边IntEdges[0](4,7,7),找4和7树根,通过FindRoot函数

n := FindRoot(&parent, IntEdges[0].begin)
m := FindRoot(&parent, IntEdges[0].end)

发现n=4,m=7 n!=m 说明他们两个之前不在一个生成树中,那么就把这条边添加进来吧,注意不要忘了,parent[n]=m或者parent[m]=n,任选一种方向即可,于是乎最初的图变成了这样:
在这里插入图片描述
接下来的每次循环,小生成树会越长越大,而且他们还会合并,像小泡泡变成大泡泡一样,最后这些树,变成了一颗树,而且还包括图中所有的顶点,这样我们的最小生成树就长成啦😊。

源代码:

数据结构定义:

// 定义边
type Edge struct {
	//顶点
	Vex1, Vex2 VexType
	//权值
	Weight int
}

// Graph 定义网数据结构
type Graph struct {
	Vexes           []VexType
	Arc             [][]int
	NumVex, NumEdge int
}

// WGInit 初始化
func WGInit(G *Graph, vexs []VexType) {
	G.Vexes = make([]VexType, len(vexs))
	G.Arc = make([][]int, len(vexs))
	for i := 0; i < len(vexs); i++ {
		G.Arc[i] = make([]int, len(vexs))
	}
	for i := 0; i < len(vexs); i++ {
		for j := 0; j < len(vexs); j++ {
			if i == j {
				G.Arc[i][j] = 0
			} else {
				G.Arc[i][j] = INF
			}

		}
	}
	G.NumVex = 0
	G.NumEdge = 0
}

func WGCreat(G *Graph, vexs []VexType, edges []Edge) {

	G.NumEdge = len(edges)
	G.NumVex = len(vexs)
	//顶点集
	for i := 0; i < G.NumVex; i++ {
		G.Vexes[i] = vexs[i]
	}
	//边集合
	for _, edge := range edges {
		v1index := VexIndex(vexs, edge.Vex1)
		v2index := VexIndex(vexs, edge.Vex2)
		G.Arc[v1index][v2index] = edge.Weight
		G.Arc[v2index][v1index] = edge.Weight

	}
}

算法实现:

//排序边
type IntEdge struct {
	begin, end int
	weight     int
}

func MiniSpanTreekruskal(G *Graph) {

	var IntEdges IntEdgeList
	IntEdges = make([]IntEdge, 0)
	parent := make([]int, G.NumVex)

	//创建边集并排序
	CreatEdges(G, &IntEdges)

	for i := 0; i < G.NumVex; i++ {
		//自己是自己的前驱
		parent[i] = i
	}
	//循环遍历每一条边
	for i := 0; i < G.NumEdge; i++ {
		n := FindRoot(&parent, IntEdges[i].begin)
		m := FindRoot(&parent, IntEdges[i].end)
		//如果边的两端返回的都是尾部且相等,则说明当前边会把生成树闭环
		if m != n {
			//将此边的节点放入下标为起点的parent中,表示此顶点加入生成树集合
			parent[n] = m
			fmt.Printf("(%d,%d) %d\n", IntEdges[i].begin, IntEdges[i].end, IntEdges[i].weight)
		}
	}

	//给IntEdges排序

}

// FindRoot 查找最小生成树的连线的尾部顶点下标
func FindRoot(parent *[]int, f int) int {

	for (*parent)[f] != f {
		f = (*parent)[f]
	}
	return f
}

func CreatEdges(G *Graph, intEdges *IntEdgeList) {

	//给IntEdges赋值
	for i := 0; i < G.NumVex; i++ {
		for j := i; j < G.NumVex; j++ {
			if G.Arc[i][j] != 0 && G.Arc[i][j] != INF {
				edg := IntEdge{begin: i, end: j, weight: G.Arc[i][j]}
				*intEdges = append(*intEdges, edg)
			}
		}
	}
	sort.Sort(*intEdges)
}

测试代码:

func MiniTree() {
	//顶点集合
	vexs := []VexType{"v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8"}
	//边集合
	edges := []Edge{
		{"v0", "v1", 10},
		{"v0", "v5", 11},
		{"v1", "v2", 18},
		{"v1", "v6", 16},
		{"v1", "v8", 12},
		{"v2", "v3", 22},
		{"v2", "v8", 8},
		{"v3", "v8", 21},
		{"v3", "v6", 24},
		{"v3", "v7", 16},
		{"v3", "v4", 20},
		{"v4", "v5", 26},
		{"v4", "v7", 7},
		{"v5", "v6", 17},
		{"v6", "v7", 19},
	}

	var graph Graph
	WGInit(&graph, vexs)
	WGCreat(&graph, vexs, edges)
	//最小生成树kruskal算法
	MiniSpanTreekruskal(&graph)

}

输出:

(4,7) 7
(2,8) 8
(0,1) 10
(0,5) 11
(1,8) 12
(1,6) 16
(3,7) 16
(6,7) 19
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
克鲁斯卡尔算法是一种用于求解最小生成树的算法。以下是C语言实现克鲁斯卡尔算法代码,其中使用了并查集来判断是否形成环路: ``` #include <stdio.h> #include <stdlib.h> #define MAX_EDGE_NUM 20 #define MAX_VERTEX_NUM 10 typedef struct Edge { int u, v; // 边的两个顶点 int weight; // 边的权值 } Edge; int Find(int *parent, int f) { while (parent[f] > 0) { f = parent[f]; } return f; } void Union(int *parent, int x, int y) { parent[x] += parent[y]; parent[y] = x; } void Kruskal(Edge *edges, int n, int e) { int i, j, u, v; int k = 0; int m = 0; int *parent = (int*)malloc((n+1)*sizeof(int)); Edge *result = (Edge*)malloc(n*sizeof(Edge)); for (i = 0; i <= n; ++i) { parent[i] = -1; } for (i = 1; i <= e; ++i) { u = edges[i].u; v = edges[i].v; if (Find(parent, u) != Find(parent, v)) { Union(parent, Find(parent, u), Find(parent, v)); result[k++] = edges[i]; } if (k == n-1) { break; } } printf("The result of Kruskal algorithm:\n"); for (i = 0; i < k; ++i) { printf("(%d, %d) %d\n", result[i].u, result[i].v, result[i].weight); m += result[i].weight; } printf("The minimum cost is %d\n", m); free(parent); free(result); } int main() { Edge edges[MAX_EDGE_NUM]; int n, e; int i; printf("Please input the number of vertexes and edges:\n"); scanf("%d%d", &n, &e); printf("Please input all edges with their weights:\n"); for (i = 1; i <= e; ++i) { scanf("%d%d%d", &edges[i].u, &edges[i].v, &edges[i].weight); } Kruskal(edges, n, e); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值