博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
最小生成树之Prim算法和Kruskal算法
阅读量:7235 次
发布时间:2019-06-29

本文共 3509 字,大约阅读时间需要 11 分钟。

一个连通图可能有多棵生成树,而最小生成树是一副连通加权无向图中一颗权值最小的生成树,它可以根据Prim算法和Kruskal算法得出,这两个算法分别从点和边的角度来解决。

Prim算法

  1. 输入:一个加权连通图,其中顶点集合为V,边集合为E;
  2. 初始化:Vn = {x},其中x为集合V中的任一节点(起始点),Enew = {};
  3. 重复下列操作,直到Vn = V:(在集合E中选取权值最小的边(u, v),其中u为集合Vn中的元素,而v则是V中没有加入Vn的顶点(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
    将v加入集合Vn中,将(u, v)加入集合En中;)
  4. 输出:使用集合Vn和En来描述所得到的最小生成树。
以下面这张图作为例子,表格中的Vertex、Kown、Cost、Path分别表示顶点信息、是否访问过,权值,到达路径;
Prim算法
我们随机的选择顶点0作为起点,其执行步骤为:
步骤 选中结点
顶点0作为起始点 0
根据(6, 7, 8)的方案选中6 1
根据顶点1能够到达的权值(7, 4, 3)和顶点0能够到达的权值(7, 8)中选择3 5
根据顶点5能够到达的权值(8)和根据顶点1能够到达的权值(7, 4)和顶点0能够到达的权值(7, 8)中选择4 6
根据顶点6能够到达的权值(6, 7)和顶点0能够到达的权值(7)中选择6 2
根据顶点0能够到达的权值(7)和顶点6能够到达的权值(7)中选择7 4
根据顶点6能够到达的权值(7)选择7 7
根据顶点7能够到达的权值(2)选择2 3
全部结点都访问过,退出  

最终得到下面的结果,其中Path中的-1表示其作为起始点;Prim算法

Prim算法实现

根据前面的那幅图来实现,如下:
class MST(object):    def __init__(self, graph):        self.graph = graph        self.N = len(self.graph)        pass    def prim(self, start):        index = start        cost, path = [0] * self.N, [0] * self.N        # 初始化起点        known = [x for x in map(lambda x: True if x == start else False, [x for x in range(self.N)])]        path[start] = -1        for i in range(self.N):            cost[i] = self.graph[start][i]        # 遍历其余各个结点        for i in range(1, self.N):            mi = 1e9            # 找出相对最小权重的结点            for j in range(self.N):                if not known[j] and mi > cost[j]:                    mi, index = cost[j], j            # 计算路径值            for j in range(self.N):                if self.graph[j][index] == mi:                    path[index] = j            known[index] = True            # 更新index连通其它结点的权重            for j in range(self.N):                if not known[j] and cost[j] > self.graph[index][j]:                    cost[j] = self.graph[index][j]        print(path)# 图用临接矩阵表示MST([    [1e9, 6, 8, 1e9, 7, 1e9, 1e9, 1e9],    [6, 1e9, 7, 1e9, 1e9, 3, 4, 1e9],    [8, 7, 1e9, 1e9, 1e9, 1e9, 6, 1e9],    [1e9, 1e9, 1e9, 1e9, 1e9, 1e9, 1e9, 2],    [7, 1e9, 1e9, 1e9, 1e9, 1e9, 1e9, 1e9],    [1e9, 3, 1e9, 1e9, 1e9, 1e9, 1e9, 9],    [1e9, 4, 6, 1e9, 1e9, 1e9, 1e9, 7],    [1e9, 1e9, 1e9, 2, 1e9, 9, 7, 1e9],]).prim(0)
path结果为:[-1, 0, 6, 7, 0, 1, 1, 6]

Kruskal算法

构造一个只含n个顶点,而边集为空的子图,若将该子图中各个顶点看成是各棵树的根节点,则它是一个含有n棵树的森林 。之后,从图的边集中选取一条权值最小的边,若该边的两个顶点分属不同的树 ,则将其加入子图,也就是这两个顶点分别所在的 两棵树合成一棵树;反之,若该边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。依次类推,直至森林只有一棵树。kruskal算法能够在并查集的基础很快的实现。
以下面这张图作为例子,其中左边的表格是一个并查集,表示可以连通的结点。我们首先要根据权值对每条边进行排序,接着开始处理每一条边的情况。
最终得到下面的结果图:

Kruskal算法实现

因为我们要处理边,所以需要建立边的数据结构,并且要从给定的图中获取每一条边的数据。
class Edge(object):    def __init__(self, start, end, weight):        self.start = start        self.end = end        self.weight = weight    def getEdges(self):        edges = []        for i in range(self.vertex):            for j in range(i+1, self.vertex):                if self.graph[i][j] != 1e9:                    edge = Edge(i, j, self.graph[i][j])                    edges.append(edge)        return edges
接下来就是kruskal函数:
def kruskal(self):	union = dict.fromkeys([i for i in range(self.vertex)], -1)  # 辅助数组,判断两个结点是否连通	self.edges = self.getEdges()	self.edges.sort(key=lambda x: x.weight)	res = []	def getend(start):		while union[start] >= 0:			start = union[start]		return start	for edge in self.edges:		# 找到连通线路的最后一个结点		n1 = getend(edge.start)		n2 = getend(edge.end)		# 如果为共同的终点则不处理		if n1 != n2:			print('{}----->{}'.format(n1, n2))			(n1, n2) = (n2, n1) if union[n1] < union[n2] else (n1, n2)			union[n2] += union[n1]			union[n1] = n2			res.append(edge)	print(union.values())
其中union打印出来的结果和图中是一致的,为[3, 3, 5, 6, 6, 6, -8, 3]。

你可能感兴趣的文章
你的学习标配
查看>>
58到家数据库30条军规解读
查看>>
关于mysql编码问题
查看>>
c++ typedef和#define的作用范围
查看>>
Ansible系列(七):执行过程分析、异步模式和速度优化
查看>>
正则表达式matcher.group用法
查看>>
TCP/IP(三)数据链路层~2
查看>>
使用Json让Java和C#沟通的方法
查看>>
小米路由Mini刷Breed, 潘多拉和LEDE
查看>>
tornado日志使用详解
查看>>
[Flume][Kafka]Flume 与 Kakfa结合例子(Kakfa 作为flume 的sink 输出到 Kafka topic)
查看>>
键盘游戏之canvas--用OO方式写
查看>>
Leetcode: Scramble String
查看>>
JavaWeb--中文乱码小结
查看>>
MySQL优化经验和方法汇总
查看>>
JAVA获取CLASSPATH路径--转
查看>>
Linux 下测试网卡性能命令iperf 的用法
查看>>
工作总结 datatable 里的 数据 rows Columns
查看>>
正则表达式的优先级
查看>>
利用mvn进行多环境配置
查看>>