当前位置: 首页 > news >正文

玩转python: 几个案例-掌握贪心算法

什么是贪心算法

贪心算法是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是全局最好或最优的算法策略。它不从整体最优上加以考虑,只做出在某种意义上的局部最优解。下面我们将通过几个案例来深入了解贪心算法,并分析每个案例的算法复杂度、原理及代码执行过程。

贪心算法案例分析与应用场景

本文将详细介绍两种经典的贪心算法:哈夫曼编码和Dijkstra算法。我们将探讨它们的算法原理、代码实现、执行流程以及算法复杂度,并分析它们在实际项目中的应用场景。

1. 哈夫曼编码

算法原理

哈夫曼编码是一种用于无损数据压缩的贪心算法。它通过为输入字符构建一个最优二叉树(哈夫曼树),使得树的加权路径长度最小,从而实现最优编码。

代码示例

import heapq class Node:def __init__(self, char, freq):self.char = char self.freq = freq self.left = None self.right = None def __lt__(self, other):return self.freq < other.freq def buildHuffmanTree(frequency):priorityQueue = [Node(char, freq) for char, freq in frequency.items()]heapq.heapify(priorityQueue) while len(priorityQueue) > 1:left = heapq.heappop(priorityQueue)right = heapq.heappop(priorityQueue) merged = Node(None, left.freq + right.freq)merged.left = left merged.right = right heapq.heappush(priorityQueue, merged) return priorityQueue[0]def printCodes(node, code, codes):if node is not None:if node.char is not None:codes[node.char] = code if node.left is not None:printCodes(node.left, code + "0", codes)if node.right is not None:printCodes(node.right, code + "1", codes)frequency = {'A': 5, 'B': 9, 'C': 12, 'D': 13, 'E': 16, 'F': 45}
root = buildHuffmanTree(frequency)
codes = {}
printCodes(root, "", codes)
print(codes)  # 输出: {'A': '111', 'B': '110', 'C': '10', 'D': '01', 'E': '00', 'F': ''}
执行流程
  1. 创建一个优先队列priorityQueue,将所有字符及其频率作为节点加入队列,并按频率从小到大排序。
  2. 当队列中有多于一个节点时:
    • 弹出两个频率最小的节点leftright
    • 创建一个新节点merged,其频率为leftright之和,并将leftright作为子节点。
    • 将新节点加入优先队列。
  3. 返回队列中唯一的节点,即哈夫曼树的根节点。
  4. 使用递归遍历哈夫曼树,为每个字符生成编码,并存储在字典codes中。
应用场景

哈夫曼编码常用于数据压缩领域,如文件压缩、图像压缩等。它可以有效地减少数据的存储空间和传输时间。

算法复杂度
复杂度类型时间复杂度空间复杂度
哈夫曼编码O(n log n)O(n)

2. Dijkstra算法(最短路径问题)

算法原理

Dijkstra算法是一种用于求解单源最短路径问题的贪心算法。它通过维护一个顶点集合S,逐步将距离源点最近的顶点加入S中,并更新其他顶点的距离。

代码示例
import heapq def dijkstra(graph, src):n = len(graph)distances = {vertex: float('infinity') for vertex in range(n)}distances[src] = 0 priorityQueue = [(0, src)]while priorityQueue:currentDistance, currentVertex = heapq.heappop(priorityQueue) if currentDistance > distances[currentVertex]:continue for neighbor, weight in graph[currentVertex].items():distance = currentDistance + weight if distance < distances[neighbor]:distances[neighbor] = distance heapq.heappush(priorityQueue, (distance, neighbor))return distances graph = {0: {1: 4},1: {0: 4, 2: 8, 3: 7},2: {1: 8, 3: 9},3: {1: 7, 2: 9}
}src = 0 
distances = dijkstra(graph, src)
print(distances)  # 输出: {0: 0, 1: 4, 2: 12, 3: 7}
执行流程
  1. 初始化所有顶点到源点的距离为无穷大,源点到自身的距离为0。
  2. 创建一个优先队列priorityQueue,将源点及其距离(0)加入队列。
  3. 当优先队列非空时:
    • 弹出距离最小的顶点currentVertex及其距离currentDistance
    • 如果弹出的距离大于当前已知的距离,则跳过该顶点。
    • 对于当前顶点的每个邻居:
      • 计算通过当前顶点到达邻居的距离。
      • 如果新距离小于已知距离,则更新邻居的距离,并将其加入优先队列。
  4. 返回所有顶点到源点的距离。
应用场景

Dijkstra算法常用于路径规划、网络路由等领域。它可以快速找到从起点到其他所有点的最短路径。

算法复杂度

复杂度类型时间复杂度空间复杂度
Dijkstra算法O((n + m) * log n)O(n)

3. Kruskal算法(最小生成树问题)

算法原理

Kruskal算法是一种贪心算法,用于在加权无向图中找到最小生成树。算法的基本思想是按照边的权重从小到大的顺序选择边,同时确保不形成环。

代码示例
class UnionFind:def __init__(self):self.parent = {}def find(self, x):if x not in self.parent:self.parent[x] = x elif x != self.parent[x]:self.parent[x] = self.find(self.parent[x])return self.parent[x] def union(self, x, y):rootX = self.find(x)rootY = self.find(y)if rootX != rootY:self.parent[rootY] = rootX def kruskal(n, edges):uf = UnionFind()edges.sort(key=lambda x: x[2])result = []for u, v, weight in edges:if uf.find(u) != uf.find(v):uf.union(u, v)result.append((u, v, weight))return result n = 4 
edges = [(0, 1, 10),(0, 2, 6),(0, 3, 5),(1, 3, 15),(2, 3, 4)
]mst = kruskal(n, edges)
print(mst)  # 输出: [(0, 3, 5), (0, 2, 6), (1, 3, 15)]
执行流程
  1. 初始化并查集UnionFind
  2. 对所有边按照权重从小到大排序。
  3. 对于每条边:
    • 如果边的两个顶点不在同一个集合中,则将它们合并,并添加到结果中。
  4. 返回结果,即最小生成树的所有边。
应用场景

Kruskal算法常用于网络设计、交通规划等领域,可以有效地找到连接所有顶点的最小成本的树形结构。

算法复杂度
复杂度类型时间复杂度空间复杂度
Kruskal算法O(E log E)O(V)

4. Prim算法(最小生成树问题)

算法原理

Prim算法是一种贪心算法,用于在加权无向图中找到最小生成树。算法的基本思想是从任意一个顶点开始,逐步添加距离最小的边,直到所有顶点都被包含在树中。

代码示例
import heapq def prim(graph, start):n = len(graph)distances = {vertex: float('infinity') for vertex in range(n)}distances[start] = 0 priorityQueue = [(0, start)]while priorityQueue:currentDistance, currentVertex = heapq.heappop(priorityQueue) if currentDistance > distances[currentVertex]:continue for neighbor, weight in graph[currentVertex].items():distance = currentDistance + weight if distance < distances[neighbor]:distances[neighbor] = distance heapq.heappush(priorityQueue, (distance, neighbor))return distances graph = {0: {1: 4},1: {0: 4, 2: 8, 3: 7},2: {1: 8, 3: 9},3: {1: 7, 2: 9}
}start = 0 
mst = prim(graph, start)
print(mst)  # 输出: {0: 0, 1: 4, 2: 12, 3: 7}
执行流程
  1. 初始化所有顶点到起始顶点的距离为无穷大,起始顶点到自身的距离为0。
  2. 创建一个优先队列priorityQueue,将起始顶点及其距离(0)加入队列。
  3. 当优先队列非空时:
    • 弹出距离最小的顶点currentVertex及其距离currentDistance
    • 如果弹出的距离大于当前已知的距离,则跳过该顶点。
    • 对于当前顶点的每个邻居:
      • 计算通过当前顶点到达邻居的距离。
      • 如果新距离小于已知距离,则更新邻居的距离,并将其加入优先队列。
  4. 返回所有顶点到起始顶点的距离。
应用场景

Prim算法常用于网络设计、交通规划等领域,可以有效地找到连接所有顶点的最小成本的树形结构。

算法复杂度
复杂度类型时间复杂度空间复杂度
Prim算法O(V^2)(稠密图)或O(V log V + E)(稀疏图)O(V)

5. 最大子数组和问题

算法原理

最大子数组和问题是指在给定一个整数数组中,找到一个具有最大和的连续子数组。Kadane算法是一种高效的贪心算法,可以在线性时间内解决这个问题。

代码示例
def maxSubArray(nums):max_current = max_global = nums[0]for i in range(1, len(nums)):max_current = max(nums[i], max_current + nums[i])if max_current > max_global:max_global = max_current return max_global nums = [-2,1,-3,4,-1,2,1,-5,4]
max_sum = maxSubArray(nums)
print(max_sum)  # 输出: 6 
执行流程
  1. 初始化max_currentmax_global为数组的第一个元素。
  2. 对于数组中的每个元素:
    • 更新max_current为当前元素与当前元素加上前一个max_current的最大值。
    • 如果max_current大于max_global,则更新max_global
  3. 返回max_global,即最大子数组和。
应用场景

最大子数组和问题常用于金融数据分析、信号处理等领域,可以有效地找到给定数据中的最有利的连续区间。

算法复杂度

复杂度类型时间复杂度空间复杂度
Kadane算法O(n)O(1)

6. 分数背包问题

算法原理

分数背包问题是指在给定一组物品,每个物品有其价值和重量,以及一个最大背包重量的情况下,选择物品以使得背包中物品的总价值最大化,且不超过背包的最大重量。与0/1背包问题不同,分数背包允许将物品分割成小份。

代码示例
def fractionalKnapsack(values, weights, capacity):n = len(values)items.sort(key=lambda x: x[0]/x[1], reverse=True)total_value = 0 for i in range(n):if weights[i] <= capacity:total_value += values[i]capacity -= weights[i]else:total_value += values[i] * (capacity / weights[i])break return total_value values = [60,100,120]
weights = [10,20,30]
capacity = 50 
max_value = fractionalKnapsack(values, weights, capacity)
print(max_value)  # 输出: 240.0 
执行流程
  1. 创建一个物品列表items,包含物品的价值和重量。
  2. 对物品列表按照价值/重量比降序排序。
  3. 初始化总价值total_value为0。
  4. 对于每个物品:
    • 如果物品的重量小于等于剩余容量,则将物品的价值加到总价值,并减少剩余容量。
    • 如果物品的重量大于剩余容量,则将物品的部分价值加到总价值,并结束循环。
  5. 返回总价值,即分数背包问题的最优解。
应用场景

分数背包问题常用于资源分配、投资组合优化等领域,可以有效地在有限资源下最大化总价值。

算法复杂度
复杂度类型时间复杂度空间复杂度
分数背包O(n log n)O(n)

7. 零钱找零问题(贪心算法)

算法原理

零钱找零问题是指给定一定金额的支付和几种不同面额的硬币,要求找出能够凑成该支付金额的硬币组合,使得硬币的数量最少。贪心算法是一种简单有效的解决方案,它总是优先选择面值最大的硬币。

代码示例
def coinChange(coins, amount):coins.sort(reverse=True)count = 0 remainder = amount for coin in coins:count += remainder // coin remainder %= coin if remainder == 0:break return count if remainder == 0 else -1 coins = [1, 2, 5]
amount = 11 
result = coinChange(coins, amount)
print(result)  # 输出: 3 (5+5+1)
执行流程
  1. 将硬币面值列表coins按照面值从大到小排序。
  2. 初始化硬币计数count为0,余数remainder为待找零的金额。
  3. 对于每种硬币面值:
    • 计算当前硬币可以用于找零的次数,并累加到count
    • 更新余数remainder为剩余待找零的金额。
    • 如果余数为0,则说明已经找到最优解,结束循环。
  4. 如果循环结束后余数仍不为0,则说明无法用给定的硬币面值凑成指定金额,返回-1;否则返回硬币计数count
应用场景

零钱找零问题常用于银行、超市等需要进行货币找零的场景,可以有效地减少找零所需的硬币数量。

算法复杂度
复杂度类型时间复杂度空间复杂度
贪心算法O(n)O(1)

end~希望这些案例能帮助你更深入地理解贪心算法的应用。

相关文章:

玩转python: 几个案例-掌握贪心算法

什么是贪心算法 贪心算法是一种在每一步选择中都采取在当前状态下最好或最优&#xff08;即最有利&#xff09;的选择&#xff0c;从而希望导致结果是全局最好或最优的算法策略。它不从整体最优上加以考虑&#xff0c;只做出在某种意义上的局部最优解。下面我们将通过几个案例…...

腾讯集团软件开发-后台开发方向内推

熟练掌握C/C/Java/Go等其中一门开发语言&#xff1b; TCP/UDP网络协议及相关编程、进程间通讯编程&#xff1b; 专业软件知识&#xff0c;包括算法、操作系统、软件工程、设计模式、数据结构、数据库系统、网络安全等 有一定了解的&#xff1a; 1、Python、Shell、Perl等脚本语…...

哈希碰撞攻防战——深入浅出Map/Set的底层实现

各位看官早安午安晚安呀 如果您觉得这篇文章对您有帮助的话 欢迎您一键三连&#xff0c;小编尽全力做到更好 欢迎您分享给更多人哦 今天我们来学习Map/Set的底层实现 目录 问题一&#xff1a;hash会出现负数&#xff1f;数组越界 一&#xff1a;什么是二叉搜索树&#xff1f…...

深度解析Ant Design Pro 6开发实践

深度解析Ant Design Pro 6全栈开发实践&#xff1a;从架构设计到企业级应用落地 一、Ant Design Pro 6核心特性与生态定位&#xff08;技术架构分析&#xff09; 作为Ant Design生态体系的旗舰级企业应用中台框架&#xff0c;Ant Design Pro 6基于以下技术栈实现突破性升级&am…...

用大白话解释基础框架Spring Boot——像“装修套餐”一样简单

SpringBoot是什么&#xff08;SpringBoot类似装修公司的全包套餐&#xff09; SpringBoot是Java开发者的“装修神器”&#xff0c;可以快速搭建一个应用系统&#xff0c;不用自己亲自买钉子、水泥和瓷砖&#xff08;相当于传统的Spring框架的复杂配置&#xff09;&#xff0c;…...

第十三届蓝桥杯大赛软件赛决赛C/C++ 大学 B 组

A 【2022——暴力DP / 优雅背包】-CSDN博客 B 【钟表——类日期问题】-CSDN博客 C 【卡牌——二分】-CSDN博客 D 【最大数字——DFS】-CSDN博客 E 【出差——Dijkstra】-CSDN博客 F 【费用报销——01背包】-CSDN博客 G 【故障——条件概率】-CSDN博客 H 【机房—…...

java后端开发day25--阶段项目(二)

&#xff08;以下内容全部来自上述课程&#xff09; 1.美化界面 private void initImage() {//路径分两种&#xff1a;//1.绝对路径&#xff1a;从盘符开始写的路径 D:\\aaa\\bbb\\ccc.jpg//2.相对路径&#xff1a;从当前项目开始写的路径 aaa\\bbb\\ccc.jpg//添加图片的时…...

岚图汽车2月销售8013辆,岚图知音硬核引领智能出行

据官方消息&#xff0c;岚图汽车2月销售8013辆&#xff0c;同比增长152%&#xff0c;品牌势能持续提升。其中&#xff0c;岚图知音依靠强大的产品力&#xff0c;且在OTA 2.0之后&#xff0c;其AI大模型逍遥座舱为用户带来全新的出行体验。 业内专业人士表示&#xff0c;“汽车…...

【CSS—前端快速入门】CSS 常用样式

CSS 常用 CSS 样式 1. 前端样式查询网站&#xff1a; MDN Web Docs (mozilla.org) w3school 2. border 2.1 借助 MDN 了解 border 我们借助 MDN 网站来学习 border 样式的使用&#xff1a; 2.2 border 常见属性 保存代码&#xff0c;打开页面&#xff1a; 对于标签不同样式的…...

【软考-架构】1.3、磁盘-输入输出技术-总线

GitHub地址&#xff1a;https://github.com/tyronczt/system_architect 资料&文章更新 文章目录 存储系统&#x1f4af;考试真题输入输出技术&#x1f4af;考试真题第一题第二题 存储系统 寻道时间是指磁头移动到磁道所需的时间&#xff1b; 等待时间为等待读写的扇区转到…...

Linux软连接与时区日期

软连接 使用ln命令创建软连接。 在系统中创建软连接&#xff0c;可以将文件&#xff0c;文件夹连接到其他为止。 类似于Windows系统的快捷方式。 语法&#xff1a;ln -s 参数1 参数2 -s选项&#xff0c;创建软连接。 参数1&#xff0c;被链接的文件或文件夹。 参数2&#xff0…...

(十)Mapbox GL JS 中点击 Marker 时获取与该 Marker 相关的自定义数据的解决办法

在 Mapbox GL JS 中,如果你想在点击 Marker 时获取与该 Marker 相关的自定义数据,可以通过几种方式将数据绑定到 Marker 上,并在点击时获取这些数据。以下是详细的实现方法,包含代码示例和说明。 方法一:使用 JavaScript 对象属性绑定数据 你可以直接将自定义数据绑定到 …...

PyCharm怎么集成DeepSeek

PyCharm怎么集成DeepSeek 在PyCharm中集成DeepSeek等大语言模型(LLM)可以借助一些插件或通过代码调用API的方式实现,以下为你详细介绍两种方法: 方法一:使用JetBrains AI插件(若支持DeepSeek) JetBrains推出了AI插件来集成大语言模型,不过截至2024年7月,官方插件主要…...

(七)消息队列-Kafka 序列化avro(传递)

&#xff08;七&#xff09;消息队列-Kafka 序列化avro&#xff08;传递&#xff09; 客从远方来&#xff0c;遗我双鲤鱼。呼儿烹鲤鱼&#xff0c;中有尺素书。 ——佚名《饮马长城窟行》 本文已同步CSDN、掘金平台、知乎等多个平台&#xff0c;图片依然保持最初发布的水印&…...

js基础二

JavaScript基础下 1 事件处理 JS 事件&#xff08;event&#xff09;是当用户与网页进行交互时发生的事情&#xff0c;例如单机某个链接或按钮、在文本框中输入文本、按下键盘上的某个按键、移动鼠标等等。当事件发生时&#xff0c;您可以使用 JavaScript 中的事件处理程序&a…...

WSBDF レクチア 定义2 引理3 wsbdf的乘子

定义2 引理3 wsbdf的乘子 ここまで 寝みます❓...

Qt之QStateMachine等待

在项目中经常需要等待&#xff0c;我们模拟0-30的数&#xff0c;假如我们其中5&#xff0c; 25的数需要进行等待&#xff0c;等待用户处理完自己事情后&#xff0c;按下按钮继续&#xff0c;找Qt的项目中有一个 QStateMachineqstatemmachine类提供了一个分层有限状态机。 QSta…...

Wireshark 插件开发实战指南

Wireshark 插件开发实战指南 环境搭建流程图 #mermaid-svg-XpNibno7BIyfzNn5 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-XpNibno7BIyfzNn5 .error-icon{fill:#552222;}#mermaid-svg-XpNibno7BIyfzNn5 .error-t…...

基于SpringBoot的“青少年心理健康教育网站”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“青少年心理健康教育网站”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统总体结构图 实体属性图 系统首页界…...

23-整数转罗马数字

代码 测试用例 测试结果 测试结果 12. 整数转罗马数字 中等 相关标签 相关企业 七个不同的符号代表罗马数字&#xff0c;其值如下&#xff1a; 符号值I1V5X10L50C100D500M1000 罗马数字是通过添加从最高到最低的小数位值的转换而形成的。将小数位值转换为罗马数字有以…...

别再手动删了!用Notepad++正则表达式5分钟批量清理课程目录(附实战案例)

5分钟极简正则表达式实战&#xff1a;用Notepad智能清洗杂乱课程目录 每次整理网课资源时&#xff0c;最头疼的莫过于面对几十个类似03_Python基础--循环结构实战.mp4这样的文件名。手动一个个删除序号和分类不仅耗时&#xff0c;还容易出错。上周帮同事整理200多份培训视频时&…...

Keil MDK C166工具链Watch窗口数组显示异常解决方案

1. 问题现象与影响范围解析在Keil MDK开发环境中使用C166工具链时&#xff0c;开发者可能会遇到一个棘手的调试器显示问题&#xff1a;Watch窗口中的数组和指针数值显示异常。具体表现为数组地址计算错误&#xff0c;进而导致所有数组成员的数值显示都不正确。这个问题不仅影响…...

community:CANN开源社区治理指南

前言 想象一下&#xff0c;你开发了一个很棒的算子&#xff0c;想贡献给CANN社区&#xff0c;但不知道从哪入手——怎么提Issue&#xff1f;怎么提PR&#xff1f;代码规范是什么&#xff1f;会不会被拒绝&#xff1f; 我刚接触CANN开源社区那会&#xff0c;就是这样的——写了个…...

asc-devkit:昇腾算子开发调试工具完全指南

前言 第一次写Ascend C算子&#xff0c;跑出来性能只有官方的30%&#xff0c;不知道慢在哪。后来发现了asc-devkit这个工具集&#xff0c;里面有性能分析、调试、benchmark三件套&#xff0c;一把就把瓶颈查出来了——是tiling参数设太大&#xff0c;Local Memory溢出&#xf…...

2026央国企求职哪家强?TOP机构帮你稳住铁饭碗!

引言综述随着 2026 届超 1200 万毕业生涌入就业市场&#xff0c;央国企岗位竞争愈发激烈&#xff0c;岗位竞争比持续攀升。在这样的大环境下&#xff0c;求职者的核心需求集中在系统备考规划、精准岗位匹配以及高保障面试辅导上。本次测评旨在为求职者提供客观、专业的机构对比…...

10M参数也能跑ARC与数独,Bengio团队押注「多轨迹推理」

10M 参数跑到数独 97%&#xff0c;GRAM 把递归推理改成多轨迹采样。 10M 参数&#xff0c;在大模型时代显得有些微不足道。 但 Yoshua Bengio 团队与 KAIST、Mila、NYU 研究人员提出的 GRAM&#xff0c;用这个量级的模型跑出了几组值得注意的结果。 在 Sudoku-Extreme 上准确率…...

自动化测试的最佳实践:这6个原则让你的测试脚本更稳定

在当前互联网行业快速迭代的开发模式下&#xff0c;自动化测试已经成为保障软件交付质量、提升测试效率的核心手段。据行业调研数据显示&#xff0c;成熟的互联网测试团队中&#xff0c;核心回归测试场景的自动化覆盖率已经超过80%&#xff0c;自动化测试承担了绝大部分重复性测…...

鸿蒙云端相册页面构建:我的相册横向滚动与空间占用模块详解

鸿蒙云端相册页面构建&#xff1a;我的相册横向滚动与空间占用模块详解 前言 在 HarmonyOS 6.0 应用开发中&#xff0c;云端相册类页面的相册管理和存储空间分析是用户深度使用的核心功能模块。本文将以“云端相册”应用中的“我的相册”横向滚动列表和“空间占用”存储分析模块…...

AI动态简报之技术前沿篇(2026.05.22)

&#x1f4c5; 2026年5月22日 | 关注方向&#xff1a;AI技术突破 大模型创新 AI Agent 生成式AI 多模态AI &#x1f525; 第1条&#xff1a;谷歌I/O 2026三箭齐发——Gemini 3.5 Flash速度碾压4倍、Spark全天候Agent、Omni全栈多模态 核心内容&#xff1a; 谷歌I/O 2026以…...

机器学习生产化:从Notebook到可运维ML服务的实战路径

1. 项目概述&#xff1a;当模型走出笔记本&#xff0c;真正开始“呼吸”现实空气 你有没有经历过这样的时刻&#xff1a;Jupyter Notebook里所有指标都闪闪发亮&#xff0c;AUC 0.92&#xff0c;F1 0.87&#xff0c;交叉验证稳如泰山&#xff1b;业务方点头签字&#xff0c;上线…...