前端程序员学好算法系列(十)动态规划

剑指 Offer 10- I. 斐波那契数列

写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项。斐波那契数列的定义如下:

F(0) = 0, F(1) = 1

F(N) = F(N - 1) + F(N - 2), 其中 N > 1.

斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

复制代码

示例 1:

输入:n = 2

输出:1

示例 2:

输入:n = 5

输出:5

复制代码

1.斐波那契的思想,就是我们求fib(n) 的解转化为我们求 fib(n-1)+fib(n-2)  ,fib(n-1)  我们可以转化为 fib(n-1-1) + fib(n-2-1) ,随着递归进行,我们最后会得到n=1和 n=0的时候,同时我们知道 n=0时fib(0) 等于0 fib(1)等于1,

复制代码

/**

* @param {number} n

* @return {number}

*/

var fib = function(n) {

    if(n==0){

      return 0

    }

    if(n==1){

      return 1

    }

    return fib(n-1) +fib(n-2)

};

复制代码

2,上述代码实现了一个斐波那契数列,但是对于斐波那契数列数列的时间复杂度是o的n次方的,因为我们求解的时候存在大量的重复求解,在上面的基础上运用cache 缓存计算结果 cache[n] 存在时直接求解,防止重复计算,

复制代码

/**

* @param {number} n

* @return {number}

*/

var fib = function(n) {

    const cache = {

        0:0n,

        1:1n,

    };

    return Fibonacci(n) % 1000000007n;

    function Fibonacci(n){

        if(cache[n]!==undefined) {

            return cache[n]

        }

        cache[n] = Fibonacci(n - 1) + Fibonacci(n - 2)

        return cache[n];

    }


};

复制代码

3.我们改造成动态规划的求解方式,自下向上的解决问题,fibonacci = 0 时 fib(0) 为0 fibonacci 为1时fib(1) =1  fibonacci[2] = fibonacci[2-1] + fibonacci[2-2] ,我们求n的解只需求解 fibonacci[n]

复制代码

/**

* @param {number} n

* @return {number}

*/

var fib = function(n) {

    let fibonacci = [0,1];

    for(let i = 2; i <= n; i ++) {

        fibonacci[i] = (fibonacci[i - 1] + fibonacci[i - 2]) % (1e9 +7);

    }

    return fibonacci[n];

};

复制代码

我们再看一个典型的斐波那契问题

70. 爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

注意:给定 n 是一个正整数。

示例 1:

复制代码

输入: 2

输出: 2

解释: 有两种方法可以爬到楼顶。

1. 1 阶 + 1 阶

2. 2 阶

复制代码

解题:该题和上题思路相同,我们就不深入讲解了,我们使用记忆结果时可以使用对象,也可以使用数组

1.自顶向下求解

复制代码

/**

* @param {number} n

* @return {number}

*/

var climbStairs = function (n,map={1:1,2:2}) {

    if (map[n]) return map[n];

    else map[n]=map[n-1]+map[n-2];

    return climbStairs(n - 1,map) + climbStairs(n - 2,map);

};

复制代码

2. 自底向上求解

复制代码

/**

* @param {number} n

* @return {number}

*/

var climbStairs = function (n) {

      let catche = [1,1]

      for(let i=2;i<=n;i++){

        catche[i] = catche[i-1] +catche[i-2]

      }

      return catche[n]

};

复制代码

343. 整数拆分

给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。

复制代码

示例 1:

输入: 2

输出: 1

解释: 2 = 1 + 1, 1 × 1 = 1。

示例 2:

输入: 10

输出: 36

解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。

复制代码

解题:

1.我们求分割n获得的最大乘积,需要求把n分成 1和n-1的成绩 2和n-2 的成绩 到n-1和1的最大成绩,在这个结果中取最大值,

2.每次n可以选择当前值,或者i*(n-i) 不在分割n,和i* 继续分割n-i的结果i*d(n-i)

3.我们定义dp对象缓存n的最大值的结果,防止重复求解

复制代码

/**

* @param {number} n

* @return {number}

*/

var integerBreak = function(n) {

    // 定义dp缓存已求解的n的最大值

    const dp = {}

    function d(n){

          if(n==1){

            return 1

          }

          if(dp[n]!==undefined){

              return dp[n]

          }

          let res = -1

          for(let i =1;i<n;i++){

            res = Math.max(res,i*(n-i),i*d(n-i))


          }

          dp[n] = res 

          return dp[n]

    }

    return d(n)

};

复制代码

解题二

状态数组dp[i]表示:数字 i 拆分为至少两个正整数之和的最大乘积。为了方便计算,dp 的长度是 n + 1,值初始化为 1。

显然dp[2]等于 1,外层循环从 3 开始遍历,一直到 n 停止。内层循环 j 从 1 开始遍历,一直到 i 之前停止,它代表着数字 i 可以拆分成 j + (i - j)。但 j * (i - j)不一定是最大乘积,因为i-j不一定大于dp[i - j](数字i-j拆分成整数之和的最大乘积),这里要选择最大的值作为 dp[i] 的结果。

空间复杂度是 O(N)O(N),时间复杂度是 O(N^2)O(N的2次方)

复制代码

/**

* @param {number} n

* @return {number}

*/

var integerBreak = function(n) {

    const dp = new Array(n + 1).fill(1);

    for (let i = 3; i <= n; ++i) {

        for (let j = 1; j < i; ++j) {

            // 每次尝试将n分割为 j+(i-j)的值

            dp[i] = Math.max(dp[i], j * (i - j), j * dp[i - j]);

        }

    }

    return dp[n];

};

复制代码

198. 打家劫舍

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

示例 1:

输入:[1,2,3,1]

输出:4

解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。

偷窃到的最高金额 = 1 + 3 = 4 。

解题:

1. 对于一个房间我们可以选择偷取也可以选择不偷取,如果偷取的话我们下次选择需要选择n+2的房间来尝试偷取,结果取最大值

2. 我们求解的值是不考虑偷取当前房间和考虑偷取当前房间的最大值 ,res = Math.max(res,nums[i]+room(nums,i+2)) , 因为i+2 可能越界,因此当nums.length<=index时我们直接return 0

3.我们用tests 储存是否已经偷过该房间,如果tests访问过直接返回值,如果没有访问过,我们把求解的n的值储存在tests中

复制代码

/**

* @param {number[]} nums

* @return {number}

*/

var rob = function(nums) {

    let tests = new Array(nums.length).fill(-1)


    function room(nums,index){

        if(nums.length<=index){

            return 0

        }

        if(tests[index]!==-1){

            return tests[index]

        }

        let res = 0

        for(let i =index;i<nums.length;i++){

            res = Math.max(res,nums[i]+room(nums,i+2))

        }

        tests[index] = res

        return tests[index]

    }


    return room(nums,0)


};

复制代码

解法2

动态规划方程:dp[n] = MAX( dp[n-1], dp[n-2] + num )

由于不可以在相邻的房屋闯入,所以在当前位置 n 房屋可盗窃的最大值,要么就是 n-1 房屋可盗窃的最大值,要么就是 n-2 房屋可盗窃的最大值加上当前房屋的值,二者之间取最大值

举例来说:1 号房间可盗窃最大值为 33 即为 dp[1]=3,2 号房间可盗窃最大值为 44 即为 dp[2]=4,3 号房间自身的值为 22 即为 num=2,那么 dp[3] = MAX( dp[2], dp[1] + num ) = MAX(4, 3+2) = 5,3 号房间可盗窃最大值为 55

时间复杂度:O(n)O(n),nn 为数组长度

复制代码

/**

* @param {number[]} nums

* @return {number}

*/

var rob = function(nums) {

    const len = nums.length;

    if(len == 0)

        return 0;

    const dp = new Array(len + 1);

    dp[0] = 0;

    dp[1] = nums[0];

    for(let i = 2; i <= len; i++) {

        dp[i] = Math.max(dp[i-1], dp[i-2] + nums[i-1]);

    }

    return dp[len];

};

复制代码

深圳网站建设www.sz886.com

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345