首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >算法题:零钱兑换

算法题:零钱兑换

作者头像
编码如写诗
发布2026-03-02 20:44:52
发布2026-03-02 20:44:52
160
举报
文章被收录于专栏:编码如写诗编码如写诗

算法题:零钱兑换

今天刷到道挺有意思的题,零钱兑换,LeetCode第322题。

题目是这样的:给你一堆不同面额的硬币,还有一个总金额,问凑出这个总金额最少需要几枚硬币。

如果凑不出来,返回-1。

听着挺简单的对吧?

但一上手就发现没那么容易。

题目理解

举个栗子,假设硬币有 [1, 2, 5] 三种面额,要凑11块钱。

怎么凑最省?

5 + 5 + 1,3枚就够了。

要是3 + 3 + 3 + 2,就要4枚,不是最优解。

所以问题就是,在所有可能的组合里,找出硬币数最少的那个。

思路分析

最暴力的解法就是枚举所有组合,然后选最小的。

这个思路看着简单,但问题是组合数量太多,会超时。

比如有n种硬币,总金额是amount,最坏情况下要算多少次?

复杂度是指数级别的,直接原地去世。

所以得想别的办法。

其实这道题是个经典的动态规划问题。

我们定义 dp[i] 为凑出金额 i 需要的最少硬币数。

那 dp[i] 怎么算呢?

对于每一种硬币面额 coin,如果 i >= coin,那么 dp[i] 可能是 dp[i-coin] + 1。

意思就是,先凑出 i-coin,再加上一枚硬币 coin,就能凑出 i。

我们要找所有可能的 coin 里,让 dp[i-coin] + 1 最小的那个。

所以状态转移方程是:

dp[i] = min(dp[i - coin] + 1) for all coin in coins

base case 是 dp[0] = 0,凑0块钱需要0枚硬币。

然后从 1 到 amount 依次计算,最后 dp[amount] 就是答案。

如果 dp[amount] 还是初始值(比如一个很大的数),说明凑不出来,返回-1。

复杂度分析

时间复杂度是 O(amount * n),n是硬币种类的数量。

空间复杂度是 O(amount),需要一个长度为 amount+1 的数组。

这个复杂度还是可以接受的。

代码实现

代码语言:javascript
复制
package main

import (
    "fmt"
    "math"
)

func coinChange(coins []int, amount int) int {
    if amount == 0 {
        return0
    }

    // dp[i] 表示凑出金额i需要的最少硬币数
    dp := make([]int, amount+1)

    // 初始化为最大值,因为要求最小值
    for i := 0; i <= amount; i++ {
        dp[i] = math.MaxInt32
    }

    // 凑出0元需要0枚硬币
    dp[0] = 0

    // 从1到amount依次计算
    for i := 1; i <= amount; i++ {
        // 尝试每种硬币
        for _, coin := range coins {
            if i >= coin && dp[i-coin] != math.MaxInt32 {
                dp[i] = min(dp[i], dp[i-coin]+1)
            }
        }
    }

    if dp[amount] == math.MaxInt32 {
        return-1
    }
    return dp[amount]
}

func min(a, b int) int {
    if a < b {
        return a
    }
    return b
}

func main() {
    // 测试用例
    coins1 := []int{1, 2, 5}
    amount1 := 11
    fmt.Printf("coins=%v, amount=%d, result=%d\n", coins1, amount1, coinChange(coins1, amount1))

    coins2 := []int{2}
    amount2 := 3
    fmt.Printf("coins=%v, amount=%d, result=%d\n", coins2, amount2, coinChange(coins2, amount2))

    coins3 := []int{1}
    amount3 := 0
    fmt.Printf("coins=%v, amount=%d, result=%d\n", coins3, amount3, coinChange(coins3, amount3))
}

注意事项

有几点容易踩坑:

第一,初始化的时候不能用 math.MaxInt64,因为加1会溢出。

第二,判断 dp[i-coin] != math.MaxInt32 这个条件很重要,不然可能会加1后还是最大值。

第三,amount为0的时候要特殊处理,直接返回0。

第四,有些硬币组合可能根本凑不出目标金额,这时候要返回-1。

这道题还有个变种,问一共有多少种凑法,那个用的是不同的状态转移。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-02-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编码如写诗 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 题目理解
  • 思路分析
  • 复杂度分析
  • 代码实现
  • 注意事项
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档