【贪心】最小生成树Kruskal算法Python实现

### 文章目录

  • [@toc](#文章目录 @[toc] 问题描述 最小生成树的性质 证明 Kruskal算法 时间复杂性 Python实现)
  • [问题描述](#文章目录 @[toc] 问题描述 最小生成树的性质 证明 Kruskal算法 时间复杂性 Python实现)
  • [最小生成树的性质](#文章目录 @[toc] 问题描述 最小生成树的性质 证明 Kruskal算法 时间复杂性 Python实现)
  • [证明](#文章目录 @[toc] 问题描述 最小生成树的性质 证明 Kruskal算法 时间复杂性 Python实现)
  • [`Kruskal`算法](#文章目录 @[toc] 问题描述 最小生成树的性质 证明 Kruskal算法 时间复杂性 Python实现)
  • [时间复杂性](#文章目录 @[toc] 问题描述 最小生成树的性质 证明 Kruskal算法 时间复杂性 Python实现)
  • [`Python`实现](#文章目录 @[toc] 问题描述 最小生成树的性质 证明 Kruskal算法 时间复杂性 Python实现)

个人主页:丷从心

系列专栏:贪心算法


问题描述

  • 设 G = ( V , E ) G = (V , E) G=(V,E)是无向连通带权图, E E E中每条边 ( v , w ) (v , w) (v,w)的权为 c v w cvw cvw
  • 如果 G G G的一个子图 G ′ G^{'} G′是一棵包含 G G G的所有顶点的树,则称 G ′ G^{'} G′为 G G G的生成树
  • 生成树上各边权的总和称为该生成树的耗费,在 G G G的所有生成树中,耗费最小的生成树称为 G G G的最小生成树

最小生成树的性质

  • 设 G = ( V , E ) G = (V , E) G=(V,E)是连通带权图, U U U是 V V V的真子集,如果 ( u , v ) ∈ E (u , v) \in E (u,v)∈E,且 u ∈ U u \in U u∈U, v ∈ V − U v \in V - U v∈V−U,且在所有这样的边中, ( u , v ) (u , v) (u,v)的权 c u v cuv cuv最小,那么一定存在 G G G的一棵最小生成树,它以 ( u , v ) (u , v) (u,v)为其中一条边
  • 这个性质有时也称为 M S T MST MST性质
证明
  • 假设 G G G的任何一棵最小生成树都不包含边 ( u , v ) (u , v) (u,v),将边 ( u , v ) (u , v) (u,v)添加到 G G G的一棵最小生成树 T T T上,将产生含有边 ( u , v ) (u , v) (u,v)的圈,并且在这个圈上有一条不同于 ( u , v ) (u , v) (u,v)的边 ( u ′ , v ′ ) (u^{'} , v^{'}) (u′,v′),使得 u ′ ∈ U u^{'} \in U u′∈U, v ′ ∈ V − U v^{'} \in V - U v′∈V−U,如下图所示
  • 将边 ( u ′ , v ′ ) (u^{'} , v^{'}) (u′,v′)删去,得到 G G G的另一棵生成树 T ′ T^{'} T′,由于 c u v ≤ c u ′ v ′ cuv \leq cu\^{'}v\^{'} cuv≤cu′v′,所以 T ′ T^{'} T′的耗费 ≤ T \leq T ≤T的耗费,于是 T ′ T^{'} T′是一棵含有边 ( u , v ) (u , v) (u,v)的最小生成树,与假设矛盾

Kruskal算法

  • 给定无向连通带权图 G = ( V , E ) G = (V , E) G=(V,E), V = {   1 , 2 , ⋯   , n   } V = \set{1 , 2 , \cdots , n} V={1,2,⋯,n}
  • 首先将 G G G的 n n n个顶点看成 n n n个孤立的连通分支,将所有的边按权从小到大排序,然后从第一条边开始,依边权递增的顺序查看每条边,并按下述方法连接两个不同的连通分支
  • 当查看到第 k k k条边 ( v , w ) (v , w) (v,w)时,如果端点 v v v和 w w w分别是当前两个不同的连通分支 T 1 T_{1} T1和 T 2 T_{2} T2中的顶点时,就用边 ( v , w ) (v , w) (v,w)将 T 1 T_{1} T1和 T 2 T_{2} T2连接成一个连通分支,然后继续查看第 k + 1 k + 1 k+1条边,如果端点 v v v和 w w w在当前的同一个连通分支中,就直接再查看第 k + 1 k + 1 k+1条边
  • 这个过程一直进行到只剩下一个连通分支时为止,此时这个连通分支就是 G G G的一棵最小生成树

时间复杂性

  • 当图的边数为 e e e时,Kruskal算法所需的时间是 O ( e log ⁡ e ) O(e \log{e}) O(eloge)
  • 当 e = Ω ( n 2 ) e = \Omega(n^{2}) e=Ω(n2)是,Kruskal算法比Prim算法差,当 e = o ( n 2 ) e = o(n^{2}) e=o(n2)时,Kruskal算法比Prim算法好得多

Python实现

python 复制代码
class Graph:
    def __init__(self, vertices):
        self.V = vertices  # 图中顶点的数量
        self.graph = []  # 存储图的边的列表

    def addEdge(self, u, v, w):
        self.graph.append([u, v, w])  # 添加边到图的边列表

    def find(self, parent, i):
        if parent[i] == i:  # 如果顶点 i 的根节点是自身, 则返回 i
            return i
        return self.find(parent, parent[i])  # 递归查找 i 的根节点

    def union(self, parent, rank, x, y):
        root_x = self.find(parent, x)  # 查找顶点 x 的根节点
        root_y = self.find(parent, y)  # 查找顶点 y 的根节点

        if rank[root_x] < rank[root_y]:  # 如果 x 的根节点的秩小于 y 的根节点的秩
            parent[root_x] = root_y  # 将 x 的根节点连接到 y 的根节点
        elif rank[root_x] > rank[root_y]:  # 如果 x 的根节点的秩大于 y 的根节点的秩
            parent[root_y] = root_x  # 将 y 的根节点连接到 x 的根节点
        else:  # 如果 x 和 y 的根节点的秩相同
            parent[root_y] = root_x  # 将 y 的根节点连接到 x 的根节点
            rank[root_x] += 1  # 增加 x 的根节点的秩

    def kruskalMST(self):
        result = []  # 存储最小生成树的边的列表
        i = 0  # 当前处理的边的索引
        e = 0  # 已经加入最小生成树的边的数量

        self.graph = sorted(self.graph, key=lambda x: x[2])  # 按照边的权重对图的边进行排序

        parent = []  # 存储顶点的父节点
        rank = []  # 存储顶点的秩
        for node in range(self.V):
            parent.append(node)  # 每个顶点的初始父节点是自身
            rank.append(0)  # 每个顶点的初始秩是 0

        while e < self.V - 1:  # 当最小生成树的边的数量小于 V - 1 时, 继续循环
            u, v, w = self.graph[i]  # 获取当前处理的边的源顶点、目标顶点和权重

            i += 1  # 增加边的索引

            x = self.find(parent, u)  # 查找 u 的根节点
            y = self.find(parent, v)  # 查找 v 的根节点

            if x != y:  # 如果 u 和 v 不在同一个连通分量中(不会形成环路)
                e += 1  # 增加已加入最小生成树的边的数量

                result.append([u, v, w])  # 将该边加入最小生成树的结果中

                self.union(parent, rank, x, y)  # 合并 u 和 v 所在的连通分量

        print('边\t\t权')

        for u, v, weight in result:
            print(f'{u} - {v}\t{weight}')  # 打印最小生成树的边和权重


g = Graph(5)

g.addEdge(0, 1, 2)
g.addEdge(0, 3, 6)
g.addEdge(1, 3, 8)
g.addEdge(1, 2, 3)
g.addEdge(1, 4, 5)
g.addEdge(2, 4, 7)
g.addEdge(3, 4, 9)

g.kruskalMST()
shell 复制代码
边		权
0 - 1	2
1 - 2	3
1 - 4	5
0 - 3	6

相关推荐
固定资产管理系统软件31 分钟前
商务局RFID固定资产管理系统的应用价值与落地要点解析
大数据·python
TheBestRucy1 小时前
Python 九阳神功:从零筑基到线程飞升
服务器·开发语言·网络·人工智能·python
lancyu2 小时前
关于多轮对话机器人的上下文Token优化和解决方案
人工智能·python·深度学习·机器学习·chatgpt·机器人·prompt
草莓橙子碗2 小时前
Claude 使用指南
开发语言·python
工具分享2 小时前
陪跑跟品爆单AI选品助手,高效跟品会赔本吗?
人工智能·python
COOLMO研究AI2 小时前
Python 如何实现 AI API 的提示词(Prompt)版本管理与热更新
人工智能·python·prompt
老郑聊AI业财智造2 小时前
Transformer 技术架构与源码分析
人工智能·python·深度学习·语言模型·架构·transformer·软件工程
程序员三藏3 小时前
自动化测试用例编写详解
自动化测试·软件测试·python·功能测试·测试工具·职场和发展·测试用例
230万光年的思念3 小时前
分子动力学模拟参考文献
python
HongXu_CaiYi4 小时前
0103-python相关-函数、文件
python