算法之钱币找零-01

1.说点啥

总是算法,算法,算法,林林总总少说也有小两百道了,各种文章和书籍也看了一些。
看到钱币找零,还是一筹莫展。
得改变一下自己的学习思路和状态.

  1. 每刷一道题,要写出各种解法和时间空间复杂度。
  2. 一定要写总结,这个很重要!

2. 题目描述

这个问题在我们的日常生活中更加普遍。假设我们有 1 元、2 元、5 元、10 元、20 元、50 元、100 元这些面额的纸币,它们的张数分别是 c1、c2、c5、c10、c20、c50、c100。我们现在要用这些钱来支付 K 元,最少要用多少张纸币呢?

3. 总结

3.1 解题思路

  1. 贪心: 从面值最大的找,不够了依次往下。 局部解不是最优解,所以要枚举所有值才是真正的答案(也就是回溯法,穷举所有答案)
  2. 动态规划: 一张纸币最多达到多少金额,两张纸币最多达到做少金额,递归下去。。

3.2 总结

  1. 贪心不行。
  2. 回溯可以。
  3. 动态规划有一维数组和二维数组两种解法,一维数组有两种解法。
  4. 递归有递归和递归回忆录两种解法。
  5. 回溯和递归是一种解法,只不过一个是思想,一个是实现。
  6. 一共有六种解法,七种实现。
  7. 第七种实现,最优。

4. 解法

4.0 贪心算法

时间:指数级别。
空间:O(n)(递归的n次空间)

let coinChange = function (coins, amount, idxCoin = 0) {
    coins = coins.sort((a, b) => b - a) // 排序

    return _c(coins, amount, idxCoin)
}

let _c = function (arr, amount, count = 0) {
    if (amount < 0) {
        return -1;
    }

    if (amount === 0) {
        return count;
    }

    for (var i = 0; i < arr.length; i++) {
        let max = Math.floor(amount / arr[i]);

        for (var j = max; j > 0; j--) {
            let res = _c(arr, amount - j * arr[i], j + count)
            if (res !== -1) {
                return res
            }
        }
    }

    return -1;
}

4.1 回溯(暴力穷举)(对4.2递归进行了剪枝)

时间:指数级别。
空间:O(n)(递归的n次空间)

let coinChange = function (coins, amount,idxCoin=0) {
    return _c(coins, amount, idxCoin)
}

let _c = function(coins, amount, idxCoin){
        if (amount === 0) {
        return 0;
    }

    if (idxCoin < coins.length && amount > 0) {
        let maxVal = Math.round(amount / coins[idxCoin]);
        let minCost = Number.MAX_SAFE_INTEGER;
        for (let x = 0; x <= maxVal; x++) {
            if (amount >= x * coins[idxCoin]) {
                let res = _c(coins, amount - x * coins[idxCoin],idxCoin + 1);
                if (res !== -1) {
                    minCost = Math.min(minCost, res + x)
                }
            }
        }

        return (minCost === Number.MAX_SAFE_INTEGER) ? -1 : minCost
    }

    return -1;
}

4.2 递归(动态规划-树形式)

时间复杂度: 指数级别(不仅仅和s有关,还和s-1,s-2的指数有关)
空间复杂度: O(s)(递归的空间内存)

let coinChange = (coins, amount) => {
    if (amount < 1) {
        return 0;
    }

    return _coin(coins, amount)
}

let _coin = (coins, amount) => {
    console.log(amount)
    if (amount < 0) {
        return -1;
    }

    if (amount === 0) {
        return 0
    }

    let min = Number.MAX_SAFE_INTEGER;
    for (let coin of coins) {
        let res = _coin(coins, amount - coin);
        if (res >= 0 && res < min) {
            min = res + 1;
        }
    }

    return min === Number.MAX_SAFE_INTEGER ? -1 : min;
}

4.3 递归+回忆录(递归优化)

时间复杂度:O(sn)
空间复杂度:O(S)

let coinChange = (coins, amount) => {
    if (amount < 1) {
        return 0
    }

    return _coin(coins, amount, new Array(amount).fill(0))
}

let _coin = (coins, amount, count) => {
    if (amount < 0) {
        return -1;
    }

    if (amount === 0) {
        return 0;
    }

    if (count[amount - 1] !== 0) {
        return count[amount - 1]
    }

    let min = Number.MAX_SAFE_INTEGER;
    for (let coin of coins) {
        let res = _coin(coins, amount - coin, count);
        if (res >= 0 && res < min) {
            min = res + 1;
        }
    }

    count[amount - 1] = (min === Number.MAX_SAFE_INTEGER) ? -1 : min;
    return count[amount - 1]
}

4.4 动态规划+二维数组

这是最基础的动态规划思想算法。
时间复杂度O(ssn)
空间复杂度:O(sn)

var coinChange = function(coins, amount) {
  if(amount === 0){
    return 0;
  }

  let n = amount+1;
  let dp = new Array(amount+1).fill(new Array(amount+1).fill(0));

  for(let i = 0; i < coins.length; i++){
    dp[0][coins[i]] = 1;
  }

  if(dp[0][n-1]){
    return 1;
  }

  for(let i = 1; i < n; i++){
    for(let j = n-1; j>=1; j--){
      dp[i][j] = dp[i-1][j];

      for(let m = 0; m < coins.length; m++){
        if(j + coins[m] <= amount && dp[i][j]){
          dp[i][j+coins[m]] = 1; 
        }
      }
    }

    if(dp[i][n-1]){
      return i+1;
    }
  }

  return -1;
};

4.5 动态规划+一维数组

时间复杂度:O(sn)
空间复杂度:O(s)

思路: dp[i] = min(dp[i-x],dp[i-y]) + 1;

var coinChange = function (coins, amount) {
    let dp = new Array(amount + 1).fill(Infinity);
    dp[0] = 0;

    for (let i = 1; i <= amount; i++) {
        for (let coin of coins) {
            if (i - coin >= 0) {
                dp[i] = Math.min(dp[i], dp[i - coin] + 1);
            }
        }
    }

    return dp[amount] === Infinity ? -1 : dp[amount];
}

4.6 动态规划+一维数组(优化)

时间复杂度:O(sn)
空间复杂度:O(s)

const coinChange = (coins, amount) => {
    let dp = new Array(amount + 1).fill(Number.MAX_SAFE_INTEGER);
    dp[0] = 0;
    for (let coin of coins) {
        for (let i = coin; i <= amount; i++) {
            dp[i] = Math.min(dp[i], dp[i - coin] + 1)
        }
    }
    return dp[amount] === Number.MAX_SAFE_INTEGER ? -1 : dp[amount]
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值