贪心算法之最小生成树问题

1. 贪心算法的基本思想

贪心算法在每一步都选择局部最优的边,希望最终得到整体最优的生成树。常见的两种 MST 算法为 Kruskal 算法Prim 算法。这两者均满足贪心选择性质和最优子结构性质,即:

  • 贪心选择性质:局部最优选择(比如选择当前权值最小的边)可以构成全局最优解。

  • 最优子结构:一个最优解包含其子问题的最优解。


2. 正确性证明

2.1 交换论证法

以 Kruskal 算法为例,正确性证明常使用“交换论证法”:

  • 假设在某一步选取了当前权值最小的边 e,若该边不在某最优解中,则存在一个边 f(在最优解中)可以与 e 交换,并且不会增加生成树的总权值。

  • 通过不断的“交换”,最终可构造出与 Kruskal 算法选取的边集合相同的生成树,从而证明其最优性。

2.2 剪枝证明(Cut Property)

  • 割定理(Cut Property):对于图中的任一割,跨割的最小边必定属于某个 MST。

  • Kruskal 算法每次选择全图中最小且不会形成环的边,正好满足割定理,从而确保了所选边集一定可以扩展为 MST。

类似地,Prim 算法从任意一个点开始,每次添加连接已构造生成树和其他顶点之间最小的边,这也遵循割定理,从而保证了正确性。


3. 算法步骤

3.1 Kruskal 算法步骤

  1. 排序:将所有边按权值从小到大排序。

  2. 初始化:每个顶点为一个独立的集合(并查集数据结构)。

  3. 遍历边集:依次取出最小边,判断其两个顶点是否在同一集合:

    • 如果不在同一集合,则将该边加入生成树,并合并两个集合;

    • 否则,跳过该边(避免环的产生)。

  4. 终止条件:当生成树边数达到 n−1(n 为顶点数)时结束。

3.2 Prim 算法步骤

  1. 初始化:任选一个顶点,将其加入 MST 集合。

  2. 维护优先队列:将所有与当前生成树相连的边加入优先队列。

  3. 选择边:从队列中取出最小边,若其另一端未被访问,则加入生成树,并将该顶点所有相连边更新到队列。

  4. 重复:直到所有顶点均已加入 MST。


4. 时间复杂度分析

4.1 Kruskal 算法

  • 排序:对所有 E 条边进行排序,时间复杂度为 O(Elog⁡E) 。

  • 合并查找:利用路径压缩和按秩合并,合并与查询的时间复杂度近似为 O(α(n))(α 为阿克曼函数的反函数,几乎看作常数)。

  • 总体:总体时间复杂度为 O(Elog⁡E),当图稀疏时可近似看作 O(Elog⁡V)。

4.2 Prim 算法

  • 利用最小堆:每次从堆中取出最小边和更新堆的操作总体复杂度为 O((E+V)log⁡V)。

  • 总体:因此总体时间复杂度为 O(Elog⁡V)。


5. 实例分析

考虑下列图:

  • 顶点集合:{A, B, C, D, E}

  • 边集合及权值:

    • A-B: 1

    • A-C: 3

    • B-C: 3

    • B-D: 6

    • C-D: 4

    • C-E: 2

    • D-E: 5

利用 Kruskal 算法构造 MST:

  1. 排序边:A-B(1), C-E(2), A-C(3), B-C(3), C-D(4), D-E(5), B-D(6)。

  2. 选边

    • A-B (1):加入,集合合并 {A, B}。

    • C-E (2):加入,集合合并 {C, E}。

    • A-C (3):A 属于 {A, B},C 属于 {C, E},加入,集合合并 {A, B, C, E}。

    • B-C (3):跳过(形成环)。

    • C-D (4):D 未加入集合,加入后合并为 {A, B, C, D, E}。

  3. 完成:共选 4 条边,即生成 MST,总权值 1+2+3+4=10。


6. Python代码举例

以下代码使用 Kruskal 算法实现 MST 求解,并展示了如何使用并查集数据结构:

class UnionFind:def __init__(self, n):self.parent = list(range(n))self.rank = [0] * ndef find(self, u):if self.parent[u] != u:self.parent[u] = self.find(self.parent[u])return self.parent[u]def union(self, u, v):root_u = self.find(u)root_v = self.find(v)if root_u == root_v:return False  # u 和 v 已经在同一集合# 按秩合并if self.rank[root_u] < self.rank[root_v]:self.parent[root_u] = root_velif self.rank[root_u] > self.rank[root_v]:self.parent[root_v] = root_uelse:self.parent[root_v] = root_uself.rank[root_u] += 1return Truedef kruskal(n, edges):"""n: 顶点数,顶点编号为 0 到 n-1edges: 边列表,每个元素 (u, v, weight)返回最小生成树的边列表及总权值"""# 按权值排序边edges.sort(key=lambda x: x[2])uf = UnionFind(n)mst = []total_weight = 0for u, v, weight in edges:if uf.union(u, v):mst.append((u, v, weight))total_weight += weightif len(mst) == n - 1:breakreturn mst, total_weight# 示例数据
# 对应上面的实例,顶点 A,B,C,D,E 分别用 0,1,2,3,4 表示
edges = [(0, 1, 1),  # A-B(0, 2, 3),  # A-C(1, 2, 3),  # B-C(1, 3, 6),  # B-D(2, 3, 4),  # C-D(2, 4, 2),  # C-E(3, 4, 5)   # D-E
]
n = 5mst, total_weight = kruskal(n, edges)
print("最小生成树边集:", mst)
print("总权值:", total_weight)

运行结果将输出 MST 边集及其总权值。例如,上述代码可能输出:

最小生成树边集: [(0, 1, 1), (2, 4, 2), (0, 2, 3), (2, 3, 4)]
总权值: 10

最小生成树边集: [(0, 1, 1), (2, 4, 2), (0, 2, 3), (2, 3, 4)] 总权值: 10


总结

  • 逻辑推理与正确性证明:贪心算法基于割定理及交换论证法保证了局部最优选择可推导出全局最优解。

  • 算法步骤:Kruskal 和 Prim 分别通过排序边或维护最小堆实现贪心选择。

  • 时间复杂度:Kruskal 算法主要为 O(Elog⁡E) ;Prim 算法为 O(Elog⁡V) 。

  • 实例与代码:通过一个实例和 Python 代码演示了 MST 的求解过程。

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

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

相关文章

LeetCode hot 100—编辑距离

题目 给你两个单词 word1 和 word2&#xff0c; 请返回将 word1 转换成 word2 所使用的最少操作数 。 你可以对一个单词进行如下三种操作&#xff1a; 插入一个字符删除一个字符替换一个字符 示例 示例 1&#xff1a; 输入&#xff1a;word1 "horse", word2 &q…

2.3 Spark运行架构与流程

Spark运行架构与流程包括几个核心概念&#xff1a;Driver负责提交应用并初始化作业&#xff0c;Executor在工作节点上执行任务&#xff0c;作业是一系列计算任务&#xff0c;任务是作业的基本执行单元&#xff0c;阶段是一组并行任务。Spark支持多种运行模式&#xff0c;包括单…

NO.82十六届蓝桥杯备战|动态规划-从记忆化搜索到动态规划|下楼梯|数字三角形(C++)

记忆化搜索 在搜索的过程中&#xff0c;如果搜索树中有很多重复的结点&#xff0c;此时可以通过⼀个"备忘录"&#xff0c;记录第⼀次搜索到的结果。当下⼀次搜索到这个结点时&#xff0c;直接在"备忘录"⾥⾯找结果。其中&#xff0c;搜索树中的⼀个⼀个结点…

使用 VBA 宏创建一个选择全部word图片快捷指令,进行图片格式编辑

使用 VBA 宏批量选择图片 ✅ 第一步&#xff1a;创建 .dotm 加载项文件 1、使用环境 office word 365&#xff0c;文件格式为.docx 图片格式为.PNG 2、创建 .dotm 加载项文件 打开 Word&#xff0c;新建一个空白文档。 按下 Alt F11 打开 VBA 编辑器。 点击菜单栏&#xff…

深度学习的下一个突破:从图像识别到情境理解

引言 过去十年&#xff0c;深度学习在图像识别领域取得了惊人的突破。从2012年ImageNet大赛上的AlexNet&#xff0c;到后来的ResNet、EfficientNet&#xff0c;再到近年来Transformer架构的崛起&#xff0c;AI已经能在许多任务上超越人类&#xff0c;比如人脸识别、目标检测、医…

使用dyn4j做碰撞检测

文章目录 前言一、环境准备添加依赖基本概念 二、实现步骤1.创建世界2.添加物体3.设置碰撞监听器4.更新世界 三、完整代码示例四、优化补充总结 前言 dyn4j 提供了高效的碰撞检测和物理模拟功能&#xff0c;适用于游戏开发、动画制作以及其他需要物理交互的场景。通过简单的 A…

VS Code settings.json 文件中常用的预定义变量‌及其用途说明

VS Code settings.json 常用预定义变量 以下是 Visual Studio Code 配置文件中常用的预定义变量列表&#xff1a; 1. 工作区相关变量 变量描述示例值${workspaceFolder}当前工作区根目录的绝对路径C:/projects/my-project${workspaceFolderBasename}工作区文件夹名称&#x…

elasticSearch-搜索引擎

搜索引擎的优势 有了数据库分页查询&#xff0c;为什么还需要搜索引擎&#xff1f; 搜索引擎速度上很快数据库分页查询&#xff0c;随着数据库数据量增大&#xff0c;页数靠后&#xff0c;会导致搜索速度变慢&#xff0c;但是搜索引擎不会搜索引擎支持分词查询&#xff0c;地…

安装OpenJDK1.8 17 (macos M芯片)

安装OpenJDK 1.8 下载完后&#xff0c;解压&#xff0c;打开 环境变量的配置文件即可 vim ~/.zshrc #export JAVA_HOME/Users/xxxxx/jdk-21.jdk/Contents/Home #export JAVA_HOME/Users/xxxxx/jdk-17.jdk/Contents/Home #export JAVA_HOME/Users/xxxxx/jdk-11.jdk/Contents…

断言与反射——以golang为例

断言 x.(T) 检查x的动态类型是否是T&#xff0c;其中x必须是接口值。 简单使用 func main() {var x interface{}x 100value1, ok : x.(int)if ok {fmt.Println(value1)}value2, ok : x.(string)if ok {//未打印fmt.Println(value2)} }需要注意如果不接受第二个参数就是OK,这…

Java设计模式:系统性解析与核心模式

一、设计模式三大分类总览 创建型模式&#xff08;5种&#xff09; 核心目标&#xff1a;对象创建的优化与解耦 单例模式&#xff08;Singleton&#xff09; 工厂模式&#xff08;Factory&#xff09; 抽象工厂模式&#xff08;Abstract Factory&#xff09; 建造者模式&#…

Elasticsearch 向量数据库,原生支持 Google Cloud Vertex AI 平台

作者&#xff1a;来自 Elastic Valerio Arvizzigno Elasticsearch 将作为第一个第三方原生语义对齐引擎&#xff0c;支持 Google Cloud 的 Vertex AI 平台和 Google 的 Gemini 模型。这使得联合用户能够基于企业数据构建完全可定制的生成式 AI 体验&#xff0c;并借助 Elastics…

408 计算机网络 知识点记忆(7)

前言 本文基于王道考研课程与湖科大计算机网络课程教学内容&#xff0c;系统梳理核心知识记忆点和框架&#xff0c;既为个人复习沉淀思考&#xff0c;亦希望能与同行者互助共进。&#xff08;PS&#xff1a;后续将持续迭代优化细节&#xff09; 往期内容 408 计算机网络 知识…

10-MySQL-性能优化思路

1、优化思路 当我们发现了一个慢SQL的问题的时候,需要做性能优化,一般我们是为了提高SQL查询更快,一个查询的流程由下图的各环节组成,每个环节都会消耗时间,要减少消耗时候需要从各个环节都分析一遍。 2 连接配置优化 第一个环节是客户端连接到服务端,这块可能会出现服务…

Docker:安装与部署 Nacos 的技术指南

1、简述 Nacos(Dynamic Naming and Configuration Service)是阿里巴巴开源的一个动态服务发现、配置管理和服务治理的综合解决方案,适用于微服务架构。 Nacos 主要功能: 服务发现与注册:支持 Dubbo、Spring Cloud 等主流微服务框架的服务发现与注册。动态配置管理:支持…

【非机动车检测】用YOLOv8实现非机动车及驾驶人佩戴安全帽检测

非机动车及驾驶人佩戴安全帽检测任务的意义主要包括以下几点&#xff1a; 保障行车安全&#xff1a;非机动车包括自行车、电动车等&#xff0c;佩戴安全帽能够有效保护骑车人头部&#xff0c;减少因交通事故造成的头部伤害风险&#xff0c;提高行车安全系数。 符合交通法规&am…

壹起航:15年深耕互联网营销,助力中国工厂出海获客

在全球化浪潮下&#xff0c;越来越多的中国工厂渴望拓展海外市场&#xff0c;但面临品牌建立、稳定询盘获取及营销成本降低等多重挑战。壹起航凭借15年的丰富经验&#xff0c;整合外贸建站、SEO优化及海外短视频营销&#xff0c;为中国工厂提供一站式出海解决方案。 一、外贸独…

Emacs 折腾日记(二十)——修改emacs的一些默认行为

上一篇我们完成了emacs输入法的配置以及将emacs配置成了使用vim的操作方式。但是emacs目前有些默认行为我不太喜欢&#xff0c;这节我们一起来修改它 备份设置 我们打开emacs的配置文件所在路径&#xff0c;发现有大量的~结尾的文件&#xff0c;这是emacs的备份文件。这里&am…

聊透多线程编程-线程基础-4.C# Thread 子线程执行完成后通知主线程执行特定动作

在多线程编程中&#xff0c;线程之间的同步和通信是一个常见的需求。例如&#xff0c;我们可能需要一个子线程完成某些任务后通知主线程&#xff0c;并由主线程执行特定的动作。本文将基于一个示例程序&#xff0c;详细讲解如何使用 AutoResetEvent 来实现这种场景。 示例代码…

【网络安全 | 项目开发】Web 安全响应头扫描器(提升网站安全性)

原创项目,未经许可,不得转载。 文章目录 项目简介工作流程示例输出技术栈项目代码使用说明项目简介 安全响应头是防止常见 Web 攻击(如点击劫持、跨站脚本攻击等)的有效防线,因此合理的配置这些头部信息对任何网站的安全至关重要。 Web 安全响应头扫描器(Security Head…