相关文章:
数据结构–图的概念
图搜索算法 - 深度优先搜索法(DFS)
图搜索算法 - 广度优先搜索法(BFS)
图搜索算法 - 拓扑排序
图搜索算法-最短路径算法-戴克斯特拉算法
图搜索算法-最短路径算法-贝尔曼-福特算法
图搜索算法-最小生成树问题-克鲁斯卡尔算法(kruskal)
普里姆(Prim)算法
普里姆(Prim)算法也属于贪心算法,它是根据寻找邻近结点最小键值来构成最小生成树,先来认识算法步骤。
(1)创建一个最小生成树集合(简称MST),跟踪记录包含在内的结点值。
(2)为图中的所有结点分配一个键值。将所有键值初始化为无穷大。将第一个结点的键值指定为0,以便首先选择它。
(3)当MST包含所有结点,程序结束,否则到步骤四。
(4)选择一个在MST中不存在且具有最小键值的结点u,并包含在MST中。
(5)更新u所有相邻结点的键值,对于每个相邻结点v,如果边u-v的权重小于v的先前键值,则将键值更新为u-v的权重。
(6)重复步骤三。
分析步骤,普里姆算法有两个循环,每个循环为结点数量N,所有时间复杂度为O(N^2),空间上同样需要保存最小生成树的结果,所以空间复杂度为O(N)。继续用之前的作为例子如下图,
手动计算来熟悉此算法。首先挑选【广州】作为出发点,初始化每个结点的键值,如表所示。
接着遍历【广州】邻近的结点(灰色表示在MST集合中),更新邻近结点的键值,如图所示。
这时候下一个最小键值的结点便可以选择【厦门】(或者【成都】),同理更新【厦门】邻近结点的键值,结果如下图所示。
看到【武汉】的键值没有改变,是因为4>3,不需要更新(图中虚线代表连接无效)。同样的方式,挑选【成都】,再次更新邻近结点的键值,如下图所示。
此时MST包含了【广州,厦门,成都】,下一个最小键值结点便是【武汉】,同理更新邻近的结点,如图所示。
这时候【上海】的键值发生变化,由原来的5变成了1。然后就继续挑选下一个结点【上海】,最后是【北京】,最终的结果如下图所示。
从图中得知最后结果与前面克鲁斯卡尔算法求解得出的结果是一致的,MST上的所有边的权重和同样是14。
现在用代码来表示算法,首先使用邻近矩阵定义图结构,创建一个【GraphArray】类,接受二维矩阵输入。
class GraphArray(): """用邻接矩阵表示图"""def __init__(self, points):self.amount = len(points) # 记录结点的总数self.points = points # 记录结点位置和值的关系# 初始化图的邻接矩阵self.graph = [[0 for _ in range(self.amount)] for _ in range(self.amount)]
然后创建一个【GraphPrimMST】类代表此算法,primMST()函数是算法主程序,min_key()函数是为了寻找最小键值的结点,最后是printMST()函数是为了在屏幕清晰地输出结果。
class GraphPrimMST(GraphArray): """普里姆算法法,输入的是有权无向图,求解最小生成树"""def printMST(self, result): print("边 \t\t 权重") # 结果输出total = 0for i in range(1, self.amount):total += self.graph[i][ result[i]]print(self.points[result[i]], "-", self.points[i], "\t", self.graph[i][ result[i]])print("总权重和:", total)def min_key(self, key, mst):# 寻找最小键值的结点下标min = float("Inf") # 默认是无穷大for v in range(self.amount): if key[v] < min and mst[v] == False: min = key[v] min_index = v return min_index def primMST(self):# 普里姆算法主程序key = [float("Inf")] * self.amount # 默认结点键值是无穷大parent = [None] * self.amount # 记录最小生成树集合的边,也就是答案key[0] = 0 # 把第一个结点作为第一个选择的结点,键值设置为0 MST = [False] * self.amount # 记录最小生成树集合已访问结点parent[0] = -1 # 根节点没有父结点,初始化为-1for _ in range(self.amount):u = self.min_key(key, MST) # 选择一个在MST中不存在且具有最小键值的结点uMST[u] = True # 记录为已访问结点for v in range(self.amount):# 当边的权重为正,而且没有被访问,若键值比原来小,则更新结点的键值if self.graph[u][v] > 0 and MST[v] == False and key[v] > self.graph[u][v]: key[v] = self.graph[u][v] # 更新结点键值parent[v] = u # 更新所选择的边self.printMST(parent)
根据例子,验证程序是否正确。
g = GraphPrimMST(["广州","厦门","成都","武汉","上海","北京"])
g.graph = [ [0, 2, 2, 3, 0, 0], [2, 0, 0, 4, 5, 0],[2, 0, 0, 8, 0, 7],[3, 4, 8, 0, 1, 9],[0, 5, 0, 1, 0, 6],[0, 0, 7, 9, 6, 0]]
g.primMST()
# ------------结果------------------
边 权重
广州 - 厦门 2
广州 - 成都 2
广州 - 武汉 3
武汉 - 上海 1
上海 - 北京 6
总权重和: 14
更多内容
想获取完整代码或更多相关图的算法内容,请查看我的书籍:《数据结构和算法基础Python语言实现》