贪心算法精解(Java实现):从理论到实战

一、贪心算法概述

贪心算法(Greedy Algorithm)是一种在每一步选择中都采取当前状态下最优决策的算法策略。它通过局部最优选择来达到全局最优解,具有高效、简洁的特点。

核心特点:

  • 局部最优选择:每一步都做出当前看来最佳的选择,即在当前状态下,不考虑整体的最优解,只关注眼前的最优决策。
  • 无后效性:当前决策不会影响后续决策,也就是说,在做出当前的最优选择后,不会改变未来状态的决策空间和决策方式。
  • 高效性:通常时间复杂度较低,相比于一些需要穷举所有可能性的算法,贪心算法能更快地得到结果。

适用场景:

  1. 问题具有最优子结构:即问题的最优解包含了其子问题的最优解。例如,在区间调度问题中,全局的最优区间选择方案包含了每个子区间的最优选择。
  2. 问题具有贪心选择性质:通过一系列局部最优选择可以得到全局最优解。这是贪心算法能够应用的关键条件。
  3. 不需要回溯或考虑所有可能性:贪心算法在每一步都直接做出选择,而不需要回头修改之前的决策,也不需要考虑所有可能的情况。

二、贪心算法基本框架(Java 实现)

import java.util.List;// 假设Problem类是存储问题相关数据的类
class Problem {private List<Item> items;public Problem(List<Item> items) {this.items = items;}public List<Item> getItems() {return items;}
}// 假设Item类是表示问题中每个元素的类
class Item {// 可以根据具体问题添加属性和方法
}// 假设Solution类是存储最终解决方案的类
class Solution {// 可以根据具体问题添加属性和方法,用于存储和操作解决方案public void add(Item item) {// 实现将item添加到解决方案中的逻辑}
}public class GreedyAlgorithm {public static Solution greedySolution(Problem problem) {// 1. 初始化解决方案Solution solution = new Solution();// 2. 对输入进行预处理(如排序)List<Item> items = preprocess(problem.getItems());// 3. 贪心选择过程for (Item item : items) {if (canSelect(item, solution)) {solution.add(item);}}// 4. 返回最终解return solution;}private static List<Item> preprocess(List<Item> items) {// 根据具体问题实现对输入数据的预处理,例如排序// 这里简单返回原数据,实际应用中需要进行处理return items;}private static boolean canSelect(Item item, Solution solution) {// 判断当前项是否能被选中// 根据具体问题实现return true;}
}

三、经典贪心算法问题

3.1 找零钱问题(Coin Change)

import java.util.Arrays;public class CoinChange {public static int minCoins(int[] coins, int amount) {Arrays.sort(coins); // 先排序,将硬币面额从小到大排列int count = 0;int index = coins.length - 1; // 从最大面额开始,因为要尽可能使用大面额的硬币以减少硬币数量while (amount > 0 && index >= 0) {if (coins[index] <= amount) {int num = amount / coins[index]; // 计算当前面额硬币的使用数量count += num; // 将使用数量累加到总硬币数中amount -= num * coins[index]; // 从总金额中减去已使用的硬币金额}index--; // 尝试下一个较小面额的硬币}return amount == 0? count : -1; // 如果金额正好用完,返回总硬币数;否则返回-1表示无法找零}
}

3.2 区间调度问题(Interval Scheduling)

import java.util.Arrays;public class IntervalScheduling {public static int maxNonOverlappingIntervals(int[][] intervals) {if (intervals == null || intervals.length == 0) return 0;// 按照结束时间排序,这样能保证每次选择的区间结束时间最早,为后续选择留出更多空间Arrays.sort(intervals, (a, b) -> a[1] - b[1]);int count = 1; // 至少有一个区间可以选择int end = intervals[0][1]; // 记录当前已选区间的结束时间for (int i = 1; i < intervals.length; i++) {if (intervals[i][0] >= end) {count++; // 找到一个不重叠的区间,区间数量加1end = intervals[i][1]; // 更新当前已选区间的结束时间}}return count;}
}

四、LeetCode 经典题目解析

4.1 买卖股票的最佳时机 II(LeetCode 122)

class Solution {public int maxProfit(int[] prices) {int profit = 0;for (int i = 1; i < prices.length; i++) {if (prices[i] > prices[i - 1]) {profit += prices[i] - prices[i - 1]; // 如果今天的价格比昨天高,就进行买卖获取利润}}return profit;}
}

这道题的贪心策略是:只要今天的股票价格比昨天高,就进行一次买卖操作,获取差价利润。因为可以进行多次买卖,所以每次价格上涨都能带来利润,最终得到的总利润就是最大利润。

4.2 跳跃游戏(LeetCode 55)

class Solution {public boolean canJump(int[] nums) {int maxReach = 0;for (int i = 0; i < nums.length; i++) {if (i > maxReach) return false; // 如果当前位置超过了能到达的最大位置,说明无法到达终点maxReach = Math.max(maxReach, i + nums[i]); // 更新能到达的最大位置if (maxReach >= nums.length - 1) return true; // 如果能到达的最大位置已经超过或等于终点,说明可以到达终点}return true;}
}

这道题的贪心策略是:在遍历数组的过程中,不断更新能到达的最大位置。如果当前位置超过了能到达的最大位置,说明无法继续前进;如果能到达的最大位置已经超过或等于终点,说明可以到达终点。

五、贪心算法解题技巧

  1. 排序预处理:大多数贪心问题需要先对输入数据进行排序
    • 按开始时间、结束时间、权重等关键属性排序。例如在区间调度问题中,按照区间的结束时间排序,能保证每次选择的区间结束时间最早,为后续选择留出更多空间。
  2. 优先队列应用:处理需要动态获取最优解的问题
    PriorityQueue<Integer> pq = new PriorityQueue<>();
    

优先队列可以在每次操作中快速获取当前的最优元素,适用于需要动态维护最优解的场景,比如在一些涉及到权重或优先级的问题中。
3. 双指针技巧:适用于区间类问题

int left = 0, right = array.length - 1;

双指针可以在数组或区间上进行高效的遍历和操作,通过两个指针的移动来解决一些与区间相关的问题,比如寻找最长不重叠区间等。
4. 贪心选择证明

  • 交换论证法:通过交换当前贪心选择和其他选择,证明贪心选择不会导致更差的结果。
  • 数学归纳法:证明对于小规模问题贪心选择是正确的,然后假设对于规模为 n 的问题贪心选择正确,证明对于规模为 n+1 的问题贪心选择也正确。
  • 反证法:假设贪心选择不是最优的,推出矛盾,从而证明贪心选择是最优的。

六、贪心算法的局限性

  1. 不能保证全局最优:某些问题无法通过贪心算法得到最优解。因为贪心算法只考虑当前的最优选择,而没有考虑整体的情况,可能会陷入局部最优解。
  2. 适用范围有限:仅适用于具有贪心选择性质的问题。如果问题不满足贪心选择性质,使用贪心算法可能得到错误的结果。
  3. 需要严格证明:必须证明贪心选择的正确性。在应用贪心算法之前,需要通过数学证明或其他方法来验证贪心策略的有效性,否则可能无法得到正确的结果。

七、实战建议

  1. 识别贪心性质:分析问题是否具有贪心选择性质。可以通过尝试一些简单的例子,观察是否可以通过局部最优选择得到全局最优解。
  2. 从简单案例入手:通过小例子验证贪心策略。在解决复杂问题之前,先从简单的情况开始,验证贪心策略的正确性和有效性。
  3. 与动态规划对比:当贪心不适用时考虑动态规划。如果发现问题不满足贪心选择性质,或者贪心算法无法得到最优解,可以考虑使用动态规划等其他算法。
  4. 边界条件检查:特别注意空输入、极值等情况。在编写代码时,要考虑到各种可能的边界情况,避免出现错误。

八、进阶练习

  1. 分发饼干(LeetCode 455)
  2. 无重叠区间(LeetCode 435)
  3. 加油站问题(LeetCode 134)
  4. 任务调度器(LeetCode 621)
// 示例:分发饼干(LeetCode 455)
import java.util.Arrays;class Solution {public int findContentChildren(int[] g, int[] s) {Arrays.sort(g); // 对孩子的胃口大小进行排序Arrays.sort(s); // 对饼干的大小进行排序int i = 0, j = 0;while (i < g.length && j < s.length) {if (s[j] >= g[i]) {i++; // 如果当前饼干能满足当前孩子的胃口,孩子数量加1}j++; // 无论是否能满足,都尝试下一块饼干}return i; // 返回能满足的孩子数量}
}

结语

贪心算法是算法设计中的重要范式,掌握它能有效解决许多实际问题。理解其核心思想并通过大量练习培养直觉是关键。记住,不是所有问题都适合贪心解法,当遇到困难时,不妨考虑动态规划等其他方法。

学习建议

  1. 从简单贪心问题入手,逐步熟悉贪心算法的应用场景和解题思路。
  2. 重点理解贪心选择性质的证明,通过练习不同的证明方法来加深对贪心算法的理解。
  3. 对比不同解法的优劣,了解贪心算法与其他算法(如动态规划)的区别和联系,在实际应用中选择最合适的算法。
  4. 参与在线编程竞赛锻炼实战能力,通过解决各种实际问题来提高自己的算法水平和编程能力。

希望这篇 Java 实现的贪心算法指南对你的学习有所帮助!如果有任何问题,欢迎在评论区留言讨论。

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

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

相关文章

深度学习框架:PyTorch使用教程 !!

文章目录 一、PyTorch框架简介 1.1 什么是PyTorch 1.2 PyTorch的优势 二、从入门到精通的PyTorch使用教程 2.1 入门阶段 2.1.1 环境安装与配置 2.1.2 Tensor基础操作 2.1.3 自动求导&#xff08;Autograd&#xff09; 2.1.4 构建神经网络&#xff08;nn模块&#xff09; 2.1.5 …

系统架构设计师:设计模式——创建型设计模式

一、创建型设计模式 创建型模式抽象了实例化过程&#xff0c;它们帮助一个系统独立于如何创建、组合和表示它的那些对象。一个类创建型模式使用继承改变被实例化的类&#xff0c;而一个对象创建型模式将实例化委托给另一个对象。 随着系统演化得越来越依赖于对象复合而不是类…

Dinero.js - 免费开源的 JavaScript 货币处理工具库,完美解决 JS 浮点数精度丢失问题

今天介绍一个在前后端处理货币的工具库&#xff0c;logo 很可爱&#xff0c;是一只蓝色的招财小猫。 本文封面图底图来自免费 AI 图库 StockCake。 Dinero.js 是一个用于货币计算的 JavaScript 工具库&#xff0c;解决开发者在金融、电商、会计等场景中处理货币时的精度丢失、…

HNUST湖南科技大学-嵌入式考试选择题题库(109道纠正详解版)

HNUST嵌入式选择题题库 1.下面哪点不是嵌入式操作系统的特点。(B) A.内核精简 B.功能强大 C.专用性强 D.高实时性 解析&#xff1a; 嵌入式操作系统特点是内核精简、专用性强、高实时性&#xff0c;而"功能强大"通常指的是通用操作系统&#x…

【工具】Windows批量文件复制教程:用BAT脚本自动化文件管理

一、引言 在日常开发与部署过程中&#xff0c;文件的自动化复制是一个非常常见的需求。无论是在构建过程、自动部署&#xff0c;还是备份任务中&#xff0c;开发者经常需要将某个目录中的 DLL、配置文件、资源文件批量复制到目标位置。相比使用图形界面的复制粘贴操作&#xf…

xray-poc编写示例

禁止未授权扫描和测试行为&#xff01;&#xff01;&#xff01; 1. SQL 时间盲注检测 (Time-Based Blind SQLi) name: generic/time-based-sqli rules:- method: GETpath: "/product?id1 AND (SELECT 1 FROM (SELECT SLEEP(5))a)--"expression: |response.status…

【Day 14】HarmonyOS分布式数据库实战

一、分布式数据库基础 1. 核心概念速记表 术语解释示例场景分布式数据库数据自动同步到同账号设备手机添加商品→平板立即显示KV数据模型键值对存储&#xff08;类似JSON&#xff09;{"cart_item1": {"name":"牛奶","price":10}}数据…

【数据结构】- 栈

前言&#xff1a; 经过了几个月的漫长岁月&#xff0c;回头时年迈的小编发现&#xff0c;数据结构的内容还没有写博客&#xff0c;于是小编赶紧停下手头的活动&#xff0c;补上博客以洗清身上的罪孽 目录 前言&#xff1a; 栈的应用 括号匹配 逆波兰表达式 数制转换 栈的实…

TDA4VM SDK J721E (RTOS/Linux) bootloaders梳理笔记

文章目录 1. 前言2. RTOS BootLoader2.1 引导模式2.2 启动序列2.2.1 流程框图2.2.2 Memory map2.3 镜像格式详解3. Linux BootLoader镜像格式详解启动流程参考1. 前言 TDA4VM的BootLoader包含两部分:RTOS的和Linux的。 2. RTOS BootLoader 这是在SoC上的所有内核运行FreeRTO…

Spring Boot + MyBatis-Plus 的现代开发模式

之前的Maven项目和本次需要的环境配置并不一样 之前使用的是&#xff1a; 传统的 MyBatis 框架&#xff08;非 Spring Boot 环境&#xff09; 手动管理 SqlSession 使用了 .xml 的 Mapper 映射文件 没有 Spring 容器管理&#xff08;没有 Service / RestController 等&…

【Quest开发】极简版!透视环境下抠出身体并能遮挡身体上的服装

前两天发了一个很复杂的版本&#xff0c;又鼓捣了一下发现完全没有必要。我之前的理解有点偏&#xff08;不是错误的但用法错了&#xff09;&#xff0c;但是有一些小伙伴收藏了&#xff0c;害怕里面的某些东西对谁有用&#xff0c;所以写了一篇新的&#xff0c;前两步配置环境…

vue 常见ui库对比(element、ant、antV等)

Element UI 1. 简介 Element UI 是一个基于 Vue 2 和 Vue 3 的企业级 UI 组件库&#xff0c;提供了丰富的组件和主题定制功能。官方网站&#xff1a;Element UI 2. 主要特点 丰富的组件&#xff1a;包括表单、表格、布局、导航、弹窗等多种组件。主题定制&#xff1a;支持主…

MATLAB画一把伞

% 伞的参数num_ribs 5; % 伞骨数量修改为5R 1; % 伞的半径height 0.5; % 伞的高度handle_length 2; % 伞柄长度semicircle_radius 0.26; % 伞柄末端半圆的半径% 生成伞叶网格theta linspace(0, 2*pi, 100);phi linspace(0, pi/2, 50);[Theta, Phi] meshgrid(theta, phi…

如何在 Go 中实现各种类型的链表?

链表是动态内存分配中最常见的数据结构之一。它由一组有限的元素组成&#xff0c;每个元素&#xff08;节点&#xff09;至少占用两块内存&#xff1a;一块用于存放数据&#xff0c;另一块用于存放指向下一个节点的指针。本文教程将说明在 Go 语言中如何借助指针和结构体类型来…

新一代机载相控阵雷达的发展

相控阵雷达以其优越的性能在军事领域中有着广阔的应用前景&#xff0c;但由于复杂的技术、昂贵的造价使其应用范围还存在一定的局限性。然而&#xff0c;国内外对相控阵技术的研究非常重视&#xff0c;并取得了丰硕的成果。 军用相控阵雷达主要分为陆基、海基和空基几种类型。 …

多数元素题解(LC:169)

169. 多数元素 核心思想&#xff08;Boyer-Moore 投票算法&#xff09;&#xff1a; 解题思路&#xff1a;可以使用 Boyer-Moore 投票算法、该算法的核心思想是&#xff1a; 维护一个候选元素和计数器、初始时计数器为 0。 遍历数组&#xff1a; 当计数器为 0 时、设置当前元…

数据库 AI 助手测评:Chat2DB、SQLFlow 等工具如何提升开发效率?

一、引言:数据库开发的 “效率革命” 正在发生 在某互联网金融公司的凌晨故障现场,资深 DBA 正满头大汗地排查一条执行超时的 SQL—— 该语句涉及 7 张核心业务表的复杂关联,因索引缺失导致全表扫描,最终引发交易系统阻塞。这类场景在传统数据库开发中屡见不鲜:据 Gartne…

【中间件】bthread效率为什么高?

bthread效率为什么更高&#xff1f; 1 基本概念 bthread是brpc中的用户态线程&#xff08;也可称为M:N线程库&#xff09;&#xff0c;目的是&#xff1a;提高程序的并发度&#xff0c;同时降低编码难度&#xff0c;在多核cpu上提供更好的scalability和cache locality。其采用…

DeepSeek V2:引入MLA机制与指令对齐

长上下文革命:Multi-Head Latent Attention(MLA)机制 传统 Transformer 的多头注意力需要缓存所有输入token的 Key 和 Value,这对长文本推理时的内存开销极为庞大。DeepSeek V2 针对这一难题提出了“Multi-Head Latent Attention”(MLA)机制。MLA 的核心思想是对多头注意…

Druid监控sql导致的内存溢出--内存分析工具MemoryAnalyzer(mat)

问题 druid监控sql在网页端显示&#xff0c;我的服务插入sql比较大&#xff0c;druid把执行过的sql保存在DruidDataSource类的成员变量JdbcDataSourceStat dataSourceStat&#xff1b; JdbcDataSourceStat类中的LinkedHashMap<String, JdbcSqlStat> sqlStatMap中&#…