动态规划学习--- 数组查找最大递增路径长度

题目描述

leetcode地址

源码

给定一个仅含数字的数组,查找严格递增子序列的长度。
比如:[5, 7, -24,12,13,2,3,12,5,6,35]
最长子序列长度是6,即[-24, 2, 3, 5, 6, 35]

方法一:递归查找

这个题的难点在于,不能一味地取比前一个值大的数,这样得不到最优解,所以,对于比前一个值大的数,需要考察取或不取2种情况。
在递归查找时,对于比前一个数小的数,直接考察下一个位置,路径不变。比前一个数大的数,需要递归2次,取当前位置和不取当前位置,取最长的路径
代码实现:

var lengthOfLIS = function(nums) {
    return recursive(0, -Infinity)
    function recursive(pos, value) {
        // 边界处理
        if(pos >= nums.length) return 0
        let path1 = 0, path2 = 0;
        // 如果当前位置比先前的值大,则要考虑取当前值和不取当前值时得到的路径长度。
        if(nums[pos] > value) {
            path1 = 1 + recursive(pos+1, nums[pos])
        }
       //不论当前位置的值大小,都需要考察不取当前值的情况
        path2 = recursive(pos+1, value)
        return Math.max(path1, path2)
    }
};

时间复杂度:O(2^n)
空间复杂度:O(n^2)
很遗憾,用此解法在leetcode上运行是无法通过的,时间复杂度太高。

方法二:带记忆能力的递归查找

这个题目的第二个难点在于,如何存储已经查找过的位置。
用一维数组存储明显是不行的,我们存储的目的是避免递归,存储的数值要满足,一定是当前位置往后查找递增子序列时的最优解,如果用一维数组存储:
(错误示例)

var lengthOfLIS = function(nums) {
    let memo = []
    return recursive(0, -Infinity)
    function recursive(pos, value) {
        // 边界处理
        if(pos >= nums.length) return 0
        // add
        if(memo[pos]) return memo[pos]
         ...
        return memo[pos] = Math.max(path1, path2)
    }
};

以[5, 7, -24,12,13,2,3,12,5,6,35]为例,记忆数组memo的值是[5, 4, 3, 3, 2, 1, 1, 1, 1, 1, 1]。
其值存在1个问题:仅能记忆从位置0开始的递增子序列,当从位置0开始计算一轮之后,memo的每一项都有了值,下一次再次尝试调用递归,就满足了if(memo[pos])return memo[pos]。也就是,记忆数组并没有记住最优解,其值就不可用。

考虑用二维数组,memo[i][j]表示前一次取的数的位置是num[i],当前这一次取的数的位置是num[j]。再次访问某个位置时,若其前一个位置也相同,则返回memo[i][j]。

/**
 * @param {number[]} nums
 * @return {number}
 */
var lengthOfLIS = function(nums) {
    let memo = []
    for(let i = 0; i < nums.length; i++) {
        memo[i] = []
    }
    return recursive(-1, 0, -Infinity)
    function recursive(prePos, pos, value) {
        // 边界处理
        if(pos >= nums.length) return 0
        if(memo[prePos+1][pos]) {
            return memo[prePos+1][pos]
        }
        let path1 = 0, path2 = 0;
        if(nums[pos] > value) {
            path1 = 1 + recursive(pos, pos+1, nums[pos])
        }
        path2 = recursive(prePos, pos+1, value)
        return memo[prePos+1][pos] = Math.max(path1, path2)
    }
};

时间复杂度:O(n^2)
空间复杂度:O(n^2)

方法三:动态规划

dp[]存储包含当前位置的最大递增子序列长度。
举例:[5, 7, -24, 2, 3,12, 5, 6, 35]
(1)确定初始状态: dp[0] = 1, 即长度为1的数组,最长递增子序列长度也为1。(或者初始态也可以设置为,任意位置都为1,其意义是每个位置的最长递增子序列是至少包含其自身的。)

截屏2021-02-03 14.51.40.png

(2)状态分析,这一步是最难的,找到规律之前,不要失去耐心,一个位置一个位置地考察。
考虑dp[1]的值,很明显,若 nums[1] > nums[0],则dp[1] = dp[0] + 1,否则dp[1] = 1(初始状态,不改变)。

截屏2021-02-03 15.47.39.png

考虑dp[2]的值,是不是nums[2] > nums[1],也同样可以得出dp[2] = dp[1] + 1,否则,dp[2] = 1呢?这里是可行的,按这个等式填上dp[2]的值。

截屏2021-02-03 15.44.33.png

考虑dp[3]的值,nums[3] > nums[2],但不能得出dp[3] = dp[2] + 1 = 3,其实你应该发现了,要满足dp[i] = dp[i-1] + 1,一定是严格递增数组才可以。
nums[3]仅仅和nums[2]比较大小是不够的,它还需要和前面位置的所有元素都做比较,那比较出大小之后如何记录dp[3]呢?dp[0]~dp[2]上的值就派上用场了。以i表示位置3,j从0走到i-1,若满足nums[i] > nums[j],则dp[i] = dp[j] + 1,否则保持不变。

截屏2021-02-03 16.28.43.png

截屏2021-02-03 16.29.08.png

截屏2021-02-03 16.29.37.png

用此思路尝试填入余下的值,并校验正确性


截屏2021-02-03 16.47.12.png

同时,也要校验dp[1]和dp[2]是否也可以按此状态转移得出。若不行,则考虑特殊处理。

代码实现:

/**
 * @param {number[]} nums
 * @return {number}
 *  */
var lengthOfLIS = function(nums) {
    let size = nums.length;
    if(size <= 1) return size
    let dp = []
    let path = 0
    for(let i = 0; i < size; i++) {
        dp[i] = 1
        for(let j = 0; j < i; j++) {
            if(nums[i] > nums[j]) {
                dp[i] = Math.max(dp[i], dp[j]+1)
            }
        }
        path = Math.max(path, dp[i])
    }
    return path;
};

时间复杂度:O(n^2)
空间复杂度:O(n)


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

推荐阅读更多精彩内容