算法—动态规划(3)

297 阅读1分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

最近学习动态规划问题,其中有这样的问题,在一个数组中找出一个或几个数,数可以重复选择,将这些选出的数进行求和得到一个我们想要的数,这样问题我们可以将其分解为由简到难,首先看数组中是否存在这样的组合,其实这样组合可能有多个,如果有这样一组数字求和为目标数据,我们在进一步将这样一组数字找出,最后我们要做的是从中选择一个用最少的数字组合成目标数字。

是否存在一组数字求和为目标数字

const canSum = (targetNumber,numbers) => {
    if(targetNumber === 0 ) return true;
    if(targetNumber < 0) return false;
    for(let num of numbers){
        const reminder = targetNumber - num
        if(canSum(reminder,numbers) === true){
            return true
        }
    }

    return false
}
  • 首先我们设计函数,这时我们不需要考虑过多,只考虑函数输入也就是参数和函数的返回值。canSum 函数接受目标值targetNumber和一个可以在其中找出一组数字这样的数组number 返回值是表示是否存在这样一组数的可能性的布尔值
  • 然后我们需要考虑子问题,如果目标数减去数组中某一个数,如果剩下的数值可以由这个数组中一个或者多个可重复数组组合,那么就说明存在一组数字的求和可以表示目标数字。
  • 设置边界条件,也就是if(targetNumber === 0 ) return true; 如果目标函数是 0 说明就是找到了,如果为负数说明没有找到
  • 用这个语句将问题进行转换 const reminder = targetNumber - num

通过缓存来降低时间复杂度

这里使用 memoization 方式来优化,这样避免重复计算。在没有优化之前,我们来看 m = 目标数字大小、n 表示数组长度,如果数组都是 1 那么构建树高度就是 m 而且每次都会尝试分支为 n 所以就是 m 个 n 相乘得到时间复杂度为 O(nm)O(n^m) 空间复杂度为 O(m)O(m),优化后时间复杂度变为O(m×n)O(m \times n)


const canSumWithMemo = (targetNumber,numbers,memo={}) => {
    if(targetNumber in memo) return memo[targetNumber];

    if(targetNumber === 0 ) return true;
    if(targetNumber < 0) return false;
    for(let num of numbers){
        const reminder = targetNumber - num
        if(canSum(reminder,numbers) === true){
            memo[targetNumber] = true;
            return true
        }
    }
    memo[targetNumber] = false;
    return false
}

const res = canSumWithMemo(300,[7,14]);
console.log(res)