数据结构奇妙旅程之贪心算法

贪心算法(Greedy Algorithm)是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是全局最好或最优的算法。贪心算法并不总是能得到最优解,但是对于很多问题,贪心算法可以得到最优解或近似最优解,并且算法效率较高。

下面通过一个经典的贪心算法问题------找零问题(Coin Change Problem)来介绍贪心算法及其Java实现。

问题描述

给定一个硬币面额数组(例如:1, 2, 5),和一个总金额,计算组成这个总金额所需的最少硬币数。

贪心算法思路

对于这个问题,一个直观的贪心策略是按照硬币面额从大到小排序,然后尽可能多地使用面额大的硬币,直到无法再用该面额的硬币为止,然后转向下一个面额的硬币。

Java代码实现

java 复制代码
import java.util.Arrays;
import java.util.Comparator;

public class GreedyAlgorithm {

    public static int minCoins(int[] coins, int amount) {
        // 排序硬币面额,从大到小
        Arrays.sort(coins, Comparator.reverseOrder());
        
        int[] dp = new int[amount + 1];
        // 初始化dp数组,初始值为一个较大的数,表示初始状态下无法组成该金额
        Arrays.fill(dp, amount + 1);
        
        // 0金额不需要硬币
        dp[0] = 0;
        
        for (int i = 1; i <= amount; i++) {
            for (int j = 0; j < coins.length; j++) {
                if (coins[j] <= i) {
                    // 状态转移方程:当前金额的最少硬币数 = Math.min(当前金额的最少硬币数, 使用一个面额为coins[j]的硬币后的最少硬币数 + 1)
                    dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
                } else {
                    // 当前硬币面额大于当前金额,跳出循环
                    break;
                }
            }
        }
        
        // 如果dp[amount]的值没有被更新过,说明无法组成该金额
        return dp[amount] > amount ? -1 : dp[amount];
    }

    public static void main(String[] args) {
        int[] coins = {1, 2, 5};
        int amount = 11;
        System.out.println("最少硬币数:" + minCoins(coins, amount));
    }
}

代码解释

  1. minCoins 方法接收一个硬币面额数组和一个总金额作为输入,返回组成该总金额所需的最少硬币数。
  2. 首先,对硬币面额数组进行降序排序。
  3. 创建一个dp数组,用于记录每个金额所需的最少硬币数。初始时,将dp数组的所有元素设置为一个较大的数(这里设置为amount + 1)。
  4. 初始化dp[0]为0,表示0金额不需要硬币。
  5. 使用两个嵌套的循环来计算每个金额所需的最少硬币数。外层循环遍历每个金额,内层循环遍历每个硬币面额。如果当前硬币面额小于等于当前金额,则更新dp[i]的值为使用当前硬币后的最少硬币数加1和原来的dp[i]的值的较小者。
  6. 最后,返回dp[amount]的值作为结果。如果dp[amount]的值没有被更新过(仍然为初始值amount + 1),则说明无法组成该金额,返回-1。

注意:这个实现实际上并不是纯粹的贪心算法,而是动态规划算法。纯粹的贪心算法不会使用dp数组来记录中间结果,而是直接根据贪心策略计算最终结果。但是,对于这个问题,直接使用贪心策略可能会导致错误的结果(例如当硬币面额数组为{1, 3, 4}时)。因此,这里使用了动态规划的思想来保证得到正确的结果。

如果你想要实现一个纯粹的贪心算法来解决这个问题,你需要确保硬币面额满足一定的条件(例如,每个硬币面额都是其他硬币面额的倍数),这样贪心策略才能得到正确的结果。但是,在一般情况下,我们更倾向于使用动态规划等更稳健的方法来解决这类问题。

相关推荐
白白白小纯3 小时前
每日算法day3—回文链表,链表分割
c语言·数据结构·算法·leetcode
zander2584 小时前
35. 搜索插入位置:从边界语义理解二分查找
数据结构·算法·leetcode
Chen—LSN5 小时前
C语言——深度理解指针(5)
c语言·数据结构·算法·排序算法
佳児素花痴╮7 小时前
树的基础知识与查找排序算法
数据结构·算法
郝学胜-神的一滴7 小时前
Python 高级编程 026:内置数据结构之骈文纵论
开发语言·数据结构·python·程序人生·软件工程
程序员老舅13 小时前
啃透 I2C 驱动开发,才算入门嵌入式 Linux 内核驱动
数据结构·驱动开发·b树·内核·嵌入式·嵌入式开发·i2c
青山木20 小时前
Hot 100 --- 搜索插入位置
java·数据结构·算法·leetcode
Livia要学习1 天前
Python2和Python3字典底层原理
数据结构
青梅橘子皮1 天前
STL---map/set... “家族“详解(从使用到底层)(1)
数据结构·算法
Adios7941 天前
搜索二维矩阵 II
java·数据结构·算法