最少硬币找零问题

472 阅读1分钟

说明

硬币找零问题是给出要找零的钱数,以及可用的硬币面额,找出有多少中找零方法,找到所需的最少的硬币个数

例子

美国有以下面额的硬币:coins = [1,5,10,25]。要找amount = 36美分的零钱,找到所需的最少硬币集合min = [...]

思路

通过循环coins比较[coins[i],...other] 集合的长度(other可以通过递归基于更小的值求解),取最短的即为最少硬币集合min

代码

let coins = [1, 5, 10, 25];
function minCoin(coins, amount) {
  //递归的时候会有很多重复的amount值需要计算,所以这里设置缓存
  let cache = {};
  function find(value) {
    // 当要找的硬币总数为0时,最少的硬币为[]
    if (value <= 0) {
      return [];
    }
    // 返回缓存
    if (cache[value]) {
      return cache[value];
    }
    // 最少硬币个数(必须保证min是数组,因为有可能下面的递归没找到符合条件的newMin)
    let min = [];
    // 新的最少硬币个数,和min比较
    let newMin;
    // 递归的值
    let newValue;
    for (let i = 0; i < coins.length; i++) {
      let coin = coins[i]; 
      // 36-1,-1,-1,-1...最终递归到最小的newValue 1- 1 = 0
      newValue = value - coin;
      if (newValue >= 0) {       
        //最终 1-1=0 newMin = []
        newMin = find(newValue);
      }
      // for循环第一轮i=0开始的时候min = [],如果有 newMin的话,直接合并当前的coin 和newValue得到的newMin
      // 当min里面有值时,循环再比较的时候,min已经是一个最少硬币的集合,而newMin+coin才是一个集合,所以使用(newMin.length+1 < min.length)比较
      // (newMin.length || !newValue) 
      //如果newMin.length证明剩余值 newValue 可以分解成更小的数组,value可以被分成[coin,...newValue]的组合
      //如果!newValue 则剩余值newVlaue===0,可以直接使用[coin]
      if(newValue>=0 && (newMin.length+1 < min.length || !min.length)&& (newMin.length || !newValue)) {
        min = [coin,...newMin]
      }
    }
    return min
  }
  return find(amount)
}