leetcode:Coin Change

本文介绍了一种解决硬币找零问题的动态规划方法,并提供了两种实现方式:一种使用数组记录中间状态,空间复杂度为O(amount);另一种采用递归方式结合HashMap减少重复计算,虽然运行较慢但在空间复杂度上有优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原题:

You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.

Example 1:
coins = [1, 2, 5], amount = 11
return 3 (11 = 5 + 5 + 1)

Example 2:
coins = [2], amount = 3
return -1.

Note:
You may assume that you have an infinite number of each kind of coin.

题意:给一些不同面额的硬币和一个数值,给出能凑出这个面额的最少的硬币数量。


解题思路:这个题很容易想到用动态规划,我的思路是,先凑出两个比这个数值小的数,然后由这两个数再凑成这个数值(在实现时遍历了所有的这样的两个数),因为是动态规划,凑这两个数值比较小的数的硬币数是可以知道的。这个算法虽然是动态规划,但是复杂度也是O(amount^2),而且空间复杂度是O(amount),结果超时。

其实总体思路是对的,但错在“遍历所有的这样的两个数”,应该是去遍历所有的硬币,而不是数,因为硬币数量远小于数值。这种算法时间复杂度是O(amount*coins.length)。

动态规划的核心思想是假设有2,3,5三种面额,需要凑成amount,那么一定是取 1+(amount-2),1+(amount-3),1+(amount-5)这三种数量中最少的,对于(amount-2),(amount-3)和(amount-5)的计算,是递归向下进行的。


代码如下:

public static int coinChange(int[] coins, int amount) {
    if (coins == null || coins.length == 0 || amount <= 0)
        return 0;
    int[] minNumber = new int[amount + 1];
    for (int i = 1; i <= amount; i++) {
        minNumber[i] = Integer.MAX_VALUE;
        for (int j = 0; j < coins.length; j++) { //遍历所有的硬币面额
            if (coins[j] <= i && minNumber[i - coins[j]] != Integer.MAX_VALUE) //要判断是否可以生成这个面额
                minNumber[i] = Integer.min(minNumber[i], 1 + minNumber[i - coins[j]]);
        }
    }
    if (minNumber[amount] == Integer.MAX_VALUE)
        return -1;
    else
        return minNumber[amount];
}

上面思路的空间复杂度是O(amount)。


还有一种递归的实现方式,空间复杂度应该小于上者,但是比上面慢多了。在使用递归的动态规划中,使用了HashMap来保存已经计算过的数据,从而避免了重复计算,理论上说使用HashMap保存时,空间复杂度会降低。总的思路跟上面类似。

代码如下:

   HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
	
	//dp还可以用hashMap存放已经计算的数据,第一次尝试
    public int coinChange(int[] coins, int amount) {
        
    	if(amount==0){
    		return 0;
    	}
    	
    	for(int coin:coins){
    		map.put(coin, 1);
    	}
    	
    	int res = getCoins(coins, amount);
    	
    	if(res==Integer.MAX_VALUE){
    		return -1;
    	}
    	
    	return res;
    }
    
    //获取指定数目的钱,需要多少硬币
    public int getCoins(int[] coins,int amount){
    	
    	if(map.containsKey(amount)){
    		return map.get(amount);
    	}
    	
    	int minVal = Integer.MAX_VALUE;
    	
    	for(int coinVal:coins){
    		
    		int cur = minVal;
    		if(amount>coinVal){
    			cur = getCoins(coins, amount-coinVal);
    		}
    		if(minVal>cur){
    			minVal = cur;
    		}
    	}
    	
    	if(minVal!=Integer.MAX_VALUE){
    		map.put(amount, minVal+1);
    		return minVal+1;
    	}else{
    		map.put(amount, Integer.MAX_VALUE);
        	return Integer.MAX_VALUE;
    	}
    	
    	
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值