You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优化零钱兑换记忆化DP解法的运行时性能?

问题场景

给定整数数组coins表示不同面额的硬币,整数amount表示目标总金额,需返回凑出该总金额所需的最少硬币数量;若不存在任何硬币组合可凑出目标金额则返回-1,题目约定每种硬币可无限次使用。

咨询问题

当前实现的硬币找零解法是否存在提速空间,如何优化运行效率?
当前解法采用记忆化回溯思路,定义dp[curr_amount]存储从当前金额curr_amount到达目标金额所需的最少额外硬币数量,代码实现如下:

def coinChange(self, coins: List[int], amount: int) -> int:
        
        if amount == 0:
            return 0
        
        dp = {}                
        def backtrack(curr_amount):            
            if (curr_amount) in dp:
                return dp[curr_amount]
            
            if curr_amount == amount:
                return 0
            
            if curr_amount > amount:
                return inf
                
            
            for coin in coins:
                if (curr_amount) not in dp:
                    dp[curr_amount] = inf
                # 遍历所有可选硬币,取所需最少硬币数
                dp[curr_amount] = min(dp[curr_amount], 1 + backtrack(curr_amount + coin))
                    
            return dp[curr_amount]
        
        res = backtrack(0)
        
        if res == inf:
            return -1
        
        return res
优化方案

该解法的核心记忆化思路是正确的,但存在多处可优化的点,优化后运行效率可提升数倍:

  • 替换记忆化存储结构:当前用字典做dp存储,哈希表的查询、写入存在额外哈希计算开销,由于所有待计算的金额范围固定为0~amount,直接用长度为amount+1的定长数组存储状态,访问时直接按下标索引,读写速度远高于字典。
  • 消除递归开销:当前自顶向下的递归写法存在函数调用栈开销,当amount值较大时还有触发Python递归深度限制的风险,可以改成自底向上的递推写法,从金额0开始逐步计算到目标金额,完全规避递归相关的额外消耗。
  • 多维度剪枝减少无效计算:
    • 预处理硬币数组,直接筛掉所有面额大于amount的硬币,这类硬币不可能出现在合法组合里,遍历属于纯无效操作;
    • 将剩余硬币按面额从大到小排序,优先尝试大面额硬币,能更早得到一个较小的硬币数参考值,后续分支如果当前累计硬币数已经超过参考值,可以直接提前终止,砍掉大量无效分支;
    • 遍历硬币时提前判断当前硬币面额是否超过剩余待凑金额,直接跳过不符合要求的硬币,避免进入无意义的递归/计算分支;
    • 原代码中if (curr_amount) not in dp的判断放在硬币遍历循环内,每次循环都会重复执行,完全可以提到循环外只判断一次,减少重复判断开销。
  • 调整状态定义简化逻辑:把原定义「从curr_amount到amount需要的最少硬币」改成「凑出金额curr_amount需要的最少硬币」,递推逻辑更直观,也能减少边界判断的代码量。

优化后的参考实现如下:

def coinChange(self, coins: List[int], amount: int) -> int:
    # 边界情况直接返回
    if amount == 0:
        return 0
    # 预处理硬币:筛掉超大额硬币,降序排序
    valid_coins = sorted([c for c in coins if c <= amount], reverse=True)
    # 用数组存dp,初始值设为amount+1(等价于不可达,因为凑amount最多用amount个1元硬币)
    dp = [amount + 1] * (amount + 1)
    dp[0] = 0
    for i in range(1, amount + 1):
        for coin in valid_coins:
            if coin <= i:
                dp[i] = min(dp[i], dp[i - coin] + 1)
    return dp[amount] if dp[amount] != amount + 1 else -1

内容的提问来源于stack exchange,提问作者Michael Xia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 15:33:16