数据结构与算法(三)贪心算法(Java)
目录
- 一、简介
- 1.1 定义
- 1.2 基本步骤
- 1.3 优缺点
- 二、经典示例
- 2.1 选择排序
- 2.2 背包问题
- 三、经典反例:找零钱
- 3.1 题目
- 3.2 解答
- 3.3 记忆化搜索实现
- 3.4 动态规划实现
一、简介
1.1 定义
贪心算法(Greedy Algorithm),又名贪婪法,是寻找 最优解问题 的常用方法。
- 将求解过程 分成若干个步骤,每个步骤都应用贪心原则,选取当前状况下最好/最有的选择(局部最有利的选择),并以此希望最后堆叠出的结果也是最好/最优的解。
1.2 基本步骤
- 步骤1:从某个初始解出发;
- 步骤2:把求解的问题分成若干个子问题;
- 步骤3:对每一子问题求解,得到子问题的局部最优解;
- 步骤4:把子问题的局部最优解合成原来问题的一个解。
1.3 优缺点
优点:
- 简单、高效,省去为了寻找最优解可能需要穷举的操作,通常作为其他算法的辅助算法来使用;
缺点:
- 不从整体上考虑其它可能情况,每次选取局部最优解,不再进行回溯处理,所以 并非一定能得到整体最优解。
二、经典示例
2.1 选择排序
没错,我们常见的选择排序就是运用了贪心算法的思想。
题目:
- 实现数字数组递增排序。
解答:
从数组的零下标开始,依次从后面找到最小的元素下标与当前位置的元素互换,这个在后面寻找最小元素的过程就是贪心的思想。
贪心策略:寻找最小的元素,(贪心地)认定此元素就是当前位置的最小元素,然后遍历每一个位置。
public void choiceSort(int[] arr) {for (int i = 0; i < arr.length; i++) {int minIndex = i;for (int j = i + 1; j < arr.length; j++) {minIndex = arr[j] < arr[minIndex] ? j : minIndex;}if (minIndex != i) {int tmp = arr[i];arr[i] = arr[minIndex];arr[minIndex] = tmp;}}
}
2.2 背包问题
题目:
有一个背包,容量由你自己输入,有n个物品,每个物品都具有容量与价值,这些都是由你自己输入的,请问,要怎么放物品到背包里,才能使得总价值最大呢,放入背包的总容量要小于等于背包的总容量。(如果一个物品放不下,则可以拆分成多个小块)
背包:M:100
物品:N:7
重量 价值
10 20
20 40
30 30
25 20
50 40
10 35
60 70
解答:
每个物品都具有自己的重量与价格,不妨计算出每个物品的单位价值。
- 单位价值: 价值/重量,即每份重量的价值。
然后我们将这些物品 按照单位价值递减排序。这样一来就简单了,只需用贪心算法,依次把最大单位价值的物品价值和重量相加 就行了。
贪心策略:单位价值最大的物品,我们假设它就是最好的,直接把它放在背包里面。
public static void main(String[] args) {int[][] items = new int[7][2];items[0][0] = 10; items[0][1] = 20;items[1][0] = 20; items[1][1] = 40;items[2][0] = 30; items[2][1] = 30;items[3][0] = 25; items[3][1] = 20;items[4][0] = 50; items[4][1] = 40;items[5][0] = 10; items[5][1] = 35;items[6][0] = 60; items[6][1] = 70;int capacity = 100;System.out.println("背包的容量:" + capacity);StringBuilder builder = new StringBuilder();for (int[] item : items) {builder.append(Arrays.toString(item));}System.out.println(items.length + " 个物品的重量、价值:" + builder.toString());int maxValue = maxValue(items, capacity);System.out.println("最大价值:" + maxValue);
}public static int maxValue(int[][] items, int capacity) {// 计算单位价值double[] prices = new double[items.length];Map<Double, List<Integer>> positionMap = new HashMap<>(items.length);for (int i = 0; i < items.length; i++) {prices[i] = 1.0 * items[i][1] / items[i][0];List<Integer> positions = positionMap.getOrDefault(prices[i], new ArrayList<>());positions.add(i);positionMap.put(prices[i], positions);}// 排序Arrays.sort(prices);int weight = 0;int maxValue = 0;for (int i = prices.length - 1; i >= 0; i--) {List<Integer> positions = positionMap.get(prices[i]);if (positions != null) {Integer position = positions.remove(0);if (positions.size() == 0) {positionMap.remove(prices[i]);}if (weight + items[position][1] < capacity) {weight += items[position][0];maxValue += items[position][1];System.out.println("重量为 " + items[position][0] + ",价值为 " + items[position][1] + " 的物品被放入背包,剩余容量:" + (capacity - weight));}}}return maxValue;
}
执行结果:

三、经典反例:找零钱
322. 零钱兑换
3.1 题目
假设你开了间小店,不能电子支付,钱柜里的货币只有 25 分、20分、10 分、5 分和 1 分 四种硬币,如果你是售货员且要找给客户 41 分钱的硬币,如何安排才能找给客人的钱既 正确 且硬币的个数又 最少?
3.2 解答
我们看到这种题目可能第一个想法就是用 贪心算法 进行解决,其实不然,由于贪心算法不能进行回溯处理,所以并不能取得最优解。
- 41 分钱按照贪心算法,先找 25分,剩余 16分,再找 10分、5分、1分,共需要 4 枚硬币。
- 实际情况下,我们只需要找两个 20分钱,再找一个 1分钱就够了,共需要 3 枚硬币。
那么不用贪心算法,应该用什么算法呢?其实有两种方法可以解决:
- 一是
贪心+回溯,即记忆化搜索。 - 二是
动态规划。
3.3 记忆化搜索实现
- 记忆化搜索实现方式,就是自顶向下遍历,先查用完一枚硬币之后还剩多少钱,再根据剩余的钱进行迭代。
代码实现需要注意以下几点:
- int[] 类型数组初始化值为0;
- 针对需要记忆的组合,不光要记忆成功的情况,失败的情况也要记录。
class Solution {public static void main(String[] args) {int amount = 41;int[] arr1 = {1, 5, 10, 20, 25};System.out.println("零钱总数为:" + amount);System.out.println("硬币面值为:" + Arrays.toString(arr1));int result1 = coinChange(arr1, amount);System.out.println("最少使用硬币数:" + result1);}public static int coinChange(int[] coins, int amount) {if (amount == 0) {return 0;}return handleCoin(coins, new int[amount], amount);}private static int handleCoin(int[] coins, int[] his, int coinAmount) {if (coinAmount < 0) {return -1;}if (coinAmount == 0) {return 0;}if (his[coinAmount - 1] != 0) {return his[coinAmount - 1];}int minCount = Integer.MAX_VALUE;for (int coin : coins) {int tmpMinCount = handleCoin(coins, his, coinAmount - coin);if (tmpMinCount != -1 && tmpMinCount + 1 < minCount) {minCount = tmpMinCount + 1;}}his[coinAmount - 1] = minCount == Integer.MAX_VALUE ? -1 : minCount;return his[coinAmount - 1];}
}
执行结果:

3.4 动态规划实现
- 动态规划实现,则是自底向上,先计算从1开始每个金额所需的最小零钱数,直到找到需要的钱数,过程中对于剩余钱数所需要的最小零钱数可以直接使用前面计算好的数据。
代码实现需要注意以下几点:
- 对于数值类型的映射,不要用 Map 类型,用 int[] 类型效率更高;
- 在循环迭代的过程中,只需要加硬币数就可以了,不用再去迭代考虑其他的组合。
class Solution {public static void main(String[] args) {int amount = 41;int[] arr1 = {1, 5, 10, 20, 25};System.out.println("零钱总数为:" + amount);System.out.println("硬币面值为:" + Arrays.toString(arr1));int result1 = coinChange(arr1, amount);System.out.println("最少使用硬币数:" + result1);}public static int coinChange(int[] coins, int amount) {// k-零钱和,v-最小零钱量int[] his = new int[amount + 1];Arrays.fill(his, amount + 1);his[0] = 0;for (int i = 1; i <= amount; i++) {for (int coin : coins) {if (coin <= i) {his[i] = Math.min(his[i], his[i - coin] + 1);}}}return his[amount] == amount + 1 ? -1 : his[amount];}
}
执行结果:

整理完毕,完结撒花~ 🌻
参考地址:
1.小白带你学—贪心算法(Greedy Algorithm),https://zhuanlan.zhihu.com/p/53334049
2.贪心算法思想详解+示例代码,https://blog.csdn.net/jj6666djdbbd/article/details/126971331
相关文章:
数据结构与算法(三)贪心算法(Java)
目录 一、简介1.1 定义1.2 基本步骤1.3 优缺点 二、经典示例2.1 选择排序2.2 背包问题 三、经典反例:找零钱3.1 题目3.2 解答3.3 记忆化搜索实现3.4 动态规划实现 一、简介 1.1 定义 贪心算法(Greedy Algorithm),又名贪婪法&…...
057-第三代软件开发-文件监视器
第三代软件开发-文件监视器 文章目录 第三代软件开发-文件监视器项目介绍文件监视器实现原理关于 QFileSystemWatcher实现代码 关键字: Qt、 Qml、 关键字3、 关键字4、 关键字5 项目介绍 欢迎来到我们的 QML & C 项目!这个项目结合了 QML&…...
二十七、微服务案例
目录 一、实现输入搜索功能 1、下载代码,在idea上打开 2、新建RequestParams类,用于接收解析请求 3、在启动类中加入客户端地址Bean,以便实现服务 4、编写搜索方法 5、新建返回分页结果类 6、实现搜索方法 7、编写控制类,…...
(C++)string类的模拟实现
愿所有美好如期而遇 前言 我们模拟实现string类不是为了去实现他,而是为了了解他内部成员函数的一些运行原理和时间复杂度,在将来我们使用时能够合理地去使用他们。 为了避免我们模拟实现的string类与全局上的string类冲突(string类也在std命名空间中)&…...
处理数据中的缺失值--删除缺少值的行
两个最主要的处理缺失值的方法是: ❏ 删除缺少值的行; ❏ 填充缺失值; 我们首先将serum_insulin的中的字段值0替换为None,可以看到缺失值的数量为374个; print(pima[serum_insulin].isnull().sum()) pima[serum_insu…...
Kotlin学习——kt里的集合,Map的各种方法之String篇
Kotlin 是一门现代但已成熟的编程语言,旨在让开发人员更幸福快乐。 它简洁、安全、可与 Java 及其他语言互操作,并提供了多种方式在多个平台间复用代码,以实现高效编程。 https://play.kotlinlang.org/byExample/01_introduction/02_Functio…...
MIT 6.824 -- MapReduce Lab
MIT 6.824 -- MapReduce Lab 环境准备实验背景实验要求测试说明流程说明 实验实现GoLand 配置代码实现对象介绍协调器启动工作线程启动Map阶段分配任务执行任务 Reduce 阶段分配任务执行任务 终止阶段 崩溃恢复 注意事项并发安全文件转换golang 知识点 测试 环境准备 从官方gi…...
创新研报|顺应全球数字化,能源企业以“双碳”为目标的转型迫在眉睫
能源行业现状及痛点分析 挑战一:数字感知能力较弱 挑战二:与业务的融合度低 挑战三:决策响应速度滞后 挑战四:价值创造有待提升 挑战五:安全风险如影随形 能源数字化转型定义及架构 能源行业数字化转型体系大体…...
Blender 连续 5 天遭受大规模 DDoS 攻击
Blender 发布公告指出,在2023年11月18日至23日期间,blender.org 网站遭受了持续的分布式拒绝服务(DDoS)攻击,攻击者通过不断发送请求导致服务器超载,使网站运营严重中断。此次攻击涉及数百个 IP 地址的僵尸…...
Python 获取本地和广域网 IP
Python 获取本地IP ,使用第三方库,比如 netifaces import netifaces as nidef get_ip_address():try:# 获取默认网络接口(通常是 eth0 或 en0)default_interface ni.gateways()[default][ni.AF_INET][1]# 获取指定网络接口的IP地…...
静态路由配置过程
静态路由 静态路由简介 路由器在转发数据时,要先在路由表(Routing Table)中在找相应的路由,才能知道数据包应该从哪个端口转发出去。路由器建立路由表基本上有以下三种途径。 (1)直连路由:路由…...
基于OGG实现MySQL实时同步
📢📢📢📣📣📣 哈喽!大家好,我是【IT邦德】,江湖人称jeames007,10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】!😜&am…...
【计算机网络笔记】多路访问控制(MAC)协议——轮转访问MAC协议
系列文章目录 什么是计算机网络? 什么是网络协议? 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能(1)——速率、带宽、延迟 计算机网络性能(2)…...
什么是好的FPGA编码风格?(3)--尽量不要使用锁存器Latch
前言 在FPGA设计中,几乎没人会主动使用锁存器Latch,但有时候不知不觉中你的设计莫名其妙地就生成了一堆Latch,而这些Latch可能会给你带来巨大的麻烦。 什么是锁存器Latch? Latch,锁存器,一种可以存储电路…...
从0开始学习JavaScript--构建强大的JavaScript图片库
在现代Web开发中,图像是不可或缺的一部分,而构建一个强大的JavaScript图片库能够有效地管理、展示和操作图像,为用户提供更丰富的视觉体验。本文将深入探讨构建JavaScript图片库的实用技巧,并通过丰富的示例代码演示如何实现各种功…...
linux复习笔记05(小滴课堂)
hell脚本与crontab定时器的运用 查看状态: 关闭服务: 开启服务: 重启服务: crontab定时器的使用: 我们可以看到没有任何任务。 编辑: 我们可以看到这个任务了。 删除所有任务: 这代表着每分钟…...
springboot函数式web
1.通常是路由(请求路径)业务 2.函数式web:路由和业务分离 一个configure类 配置bean 路由等 实现业务逻辑 这样实现了业务和路由的分离...
常见的1/2/3位数码管接线详解
今天玩数码管的时候接触到了数码管的接线,分享一下供刚开始接触的童鞋参考 首先了解什么是数码管 数码管是一种可以显示数字和其他信息的电子设备,是显示屏其中一类, 通过对其不同的管脚输入相对的电流,会使其发亮,从而…...
C++模板介绍
定义 C模板是一种编程技术,它允许程序员在编译时生成具有特定类型的函数或类,而无需在运行时进行类型检查。模板是一种泛型编程的方式,它使得程序员可以编写可适用于多种数据类型的代码,提高了代码的重用性和灵活性。 C模板可以…...
kafka kraft 集群搭建保姆级教学 包含几个踩坑点
一.为啥弃用zookeeper kafka 弃用 ZooKeeper 而采用 KRaft 的主要原因是为了改进 Kafka 集群的可靠性和可管理性。 在传统的 Kafka 架构中,ZooKeeper 用于存储和管理集群的元数据、配置信息和状态。然而,使用 ZooKeeper 作为协调服务存在一些限制和挑战…...
【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型
摘要 拍照搜题系统采用“三层管道(多模态 OCR → 语义检索 → 答案渲染)、两级检索(倒排 BM25 向量 HNSW)并以大语言模型兜底”的整体框架: 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后,分别用…...
visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...
UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...
HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...
AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...
