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

news2024/9/30 1:34:27

文章目录

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

因上努力

个人主页:丷从心

系列专栏:贪心算法

果上随缘


问题描述

  • G = ( V , E ) G = (V , E) G=(V,E)是无向连通带权图, E E E中每条边 ( v , w ) (v , w) (v,w)的权为 c [ v ] [ w ] c[v][w] c[v][w]
  • 如果 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 uU v ∈ V − U v \in V - U vVU,且在所有这样的边中, ( u , v ) (u , v) (u,v)的权 c [ u ] [ v ] c[u][v] c[u][v]最小,那么一定存在 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 uU v ′ ∈ V − U v^{'} \in V - U vVU,如下图所示

1

  • 将边 ( u ′ , v ′ ) (u^{'} , v^{'}) (u,v)删去,得到 G G G的另一棵生成树 T ′ T^{'} T,由于 c [ u ] [ v ] ≤ c [ u ′ ] [ v ′ ] c[u][v] \leq c[u^{'}][v^{'}] c[u][v]c[u][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实现

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()
边		权
0 - 1	2
1 - 2	3
1 - 4	5
0 - 3	6

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1333416.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【图神经网络 · 科研笔记5】异构信息网络,利用注意力选择元路径;利用进化邻域和社群实现自监督动态图嵌入,交叉监督对比学习;近期科研思维导图小汇总;

记录部分科研文献阅读相关内容【划重点】,主题“图神经网络”,仅学习使用。 🎯作者主页: 追光者♂🔥 🌸个人简介: 📝[1] CSDN 博客专家📝 🏆[2] 人工智能领域优质创作者🏆 🌟[3] 2023年城市之星领跑者TOP1(哈尔滨)🌿 🌿[4] 2022年度…

maven下载jar包失败

配置国内镜像 设置国内的仓库,比如: <!--阿里仓库--><mirror><id>alimaven</id><name>aliyun maven</name><url>https://maven.aliyun.com/repository/public/</url><mirrorOf>central</mirrorOf></mirror>…

CnosDB如何确保多步操作的最终一致性?

背景 在时序数据库中&#xff0c;资源的操作是一个复杂且关键的任务。这些操作通常涉及到多个步骤&#xff0c;每个步骤都可能会失败&#xff0c;导致资源处于不一致的状态。例如&#xff0c;一个用户可能想要在CnosDB集群中删除一个租户&#xff0c;这个操作可能需要删除租户…

Alnet网络分析与demo实例

参考自 up主的b站链接&#xff1a;霹雳吧啦Wz的个人空间-霹雳吧啦Wz个人主页-哔哩哔哩视频这位大佬的博客 Fun_机器学习,pytorch图像分类,工具箱-CSDN博客 数据集下载 http://download.tensorflow.org/example_images/flower_photos.tgz 包含 5 中类型的花&#xff0c;每种…

嵌入式开发——PWM高级定时器

学习目标 加强掌握PWM开发流程理解定时器与通道的关系掌握多通道配置策略掌握互补PWM配置策略掌握定时器查询方式掌握代码抽取优化策略掌握PWM调试方式学习内容 需求 点亮8个灯,采用pwm的方式。 定时器 通道 <

Netty-4-网络编程模式

我们经常听到各种各样的概念——阻塞、非阻塞、同步、异步&#xff0c;这些概念都与我们采用的网络编程模式有关。 例如&#xff0c;如果采用BIO网络编程模式&#xff0c;那么程序就具有阻塞、同步等特质。 诸如此类&#xff0c;不同的网络编程模式具有不同的特点&#xff0c…

【大数据】NiFi 的基本使用

NiFi 的基本使用 1.NiFi 的安装与使用1.1 NiFi 的安装1.2 各目录及主要文件 2.NiFi 的页面使用2.1 主页面介绍2.2 面板介绍 3.NiFi 的工作方式3.1 基本方式3.2 选择处理器3.3 组件状态3.4 组件的配置3.4.1 SETTINGS&#xff08;通用配置&#xff09;3.4.2 SCHEDULING&#xff0…

博弈论:理解决策背后的复杂动态

1.基本概念 博弈论是一门研究具有冲突和合作元素决策制定的数学理论。它不仅适用于经济学&#xff0c;还广泛应用于政治学、心理学、生物学等领域。博弈论的核心在于分析参与者&#xff08;称为“玩家”&#xff09;在特定情境下的策略选择&#xff0c;以及这些选择如何影响最…

工资发放 C语言xdoj92

题目描述&#xff1a; 公司财务要发工资现金&#xff0c;需要提前换取100元、50元、20元、10元、5元和1元的人民币&#xff0c; 请输入工资数&#xff0c;计算张数最少情况下&#xff0c;各自需要多少张。 输入格式&#xff1a;共一行&#xff0c;输入一个正整数。 输出格式&am…

游戏软件提示d3dcompiler_43.dll的五个解决方法,亲测靠谱

在使用电脑进行工作&#xff0c;玩游戏的时候&#xff0c;我们常常会遇到一些错误提示&#xff0c;其中之一就是“D3DCompiler_43.dll丢失”的提示。D3DCompiler_43.dll是一个非常重要的动态链接库文件。它是由DirectX SDK提供的&#xff0c;用于编译和优化DirectX着色器代码的…

50 个具有挑战性的概率问题 [04/50]:尝试直至首次成功

一、说明 你好&#xff0c;我最近对与概率相关的问题产生了兴趣。我偶然发现了 Frederick Mosteller 所著的《五十个具有挑战性的概率问题及其解决方案》这本书。我认为创建一个系列来讨论这些可能作为面试问题出现的迷人问题会很有趣。每篇文章仅包含 1 个问题&#xff0c;使其…

【Qt之Quick模块】6. QML语法详解_1 基础语法与三种导入语句

前言 通过以上1-5文档的介绍&#xff0c;Quick与QML的概念及QML语法、类型、文件作用等已叙述个大概&#xff0c;接下来是对QML语法进行展开来说。 其实&#xff0c;学习任何一门语言或者做任何一件事情&#xff0c;并不用一开始就要求尽善尽美&#xff0c;做个无懈可击&…

【Python从入门到进阶】45、Scrapy框架核心组件介绍

接上篇《44、Scrapy的基本介绍和安装》 上一篇我们学习了Scrapy框架的基础介绍以及环境的搭建&#xff0c;本篇我们来学习一下Scrapy框架的核心组件的使用。 下面的核心组件的介绍&#xff0c;仍是基于这幅图的机制&#xff0c;大家可以再回顾一下&#xff1a; 注&#xff1a;…

数学的雨伞下:理解世界的乐趣

这本书没有一个公式&#xff0c;却讲透了数学的本质&#xff01; 《数学的雨伞下&#xff1a;理解世界的乐趣》。一本足以刷新观念的好书&#xff0c;从超市到对数再到相对论&#xff0c;娓娓道来。对于思维空间也给出了一个更容易理解的角度。 作者&#xff1a;米卡埃尔•洛奈…

Ubuntu20.04纯命令配置PCL(点云库)

Ubuntu20.04纯命令配置PCL&#xff08;点云库&#xff09; 最近在学习点云库&#xff08;PCL&#xff09;的使用&#xff0c;第一步就是在自己的电脑安装配置PCL。 首先&#xff0c;对于ubuntu 16.04以上版本&#xff0c;可以直接使用命令进行安装&#xff0c;新建好一个文件夹…

分析冒泡排序

#include <stdio.h> int main() { int arr[10] { 2,5,1,3,6,4,7,8,9,0 }; int i 0; int j 0; for( i 0 ;i < sizeof(arr)/sizeof(arr[0]) - 1 ; i) 红色的代表数组一共有n个元素&#xff0c;则需要n-1次 { for( j 0 // 这里可以让数组从哪一…

人工智能轨道交通行业周刊-第69期(2023.12.11-12.24)

本期关键词&#xff1a;集装箱智能管理、智慧工地、智能应急机器人、车辆构造、大模型推理 1 整理涉及公众号名单 1.1 行业类 RT轨道交通人民铁道世界轨道交通资讯网铁路信号技术交流北京铁路轨道交通网上榜铁路视点ITS World轨道交通联盟VSTR铁路与城市轨道交通RailMetro轨…

UG在实体上刻字

当我们想在实体上显示文字的时候&#xff0c;需要用到文本命令&#xff0c;菜单-插入-曲线-文本 文本命令中的具体用法 当在曲线和平面上显示文字的时候&#xff0c;只需要输入文字&#xff0c;并选中相应的曲线或者平面即可 当在曲面上显示文字的时候&#xff0c;设置如下 当文…

Vue3中的混入(mixins)

本文主要介绍Vue3中的混入&#xff08;mixins&#xff09;。 目录 一、在普通写法中使用混入&#xff1a;二、在setup写法中使用混入&#xff1a; 混入是Vue中一种用于在组件中共享可复用功能的特性。在Vue 3中&#xff0c;混入的使用方式有所改变。 一、在普通写法中使用混入…

c++11--类型自动推导

1.自动类型推断 1.1.auto a.auto声明变量的类型必须由编译器在编译时期推导而得。 int main(){double foo();auto x 1;//x类型为intauto y foo();// y类型为doubleauto z;// errreturn 0; }b.auto声明得变量必须被初始化。 c.针对指针和引用 推导类型是指针类型时&#xff0…