JS:三大算法 day3

回溯、贪心、动态规划的思路是通过大量做题培养的,不是说几天就能掌握,而且题目不会告诉你使用哪个算法。坚持做题。。。

动态规划

展示代码的前提,先给出状态转移矩阵的定义

基础题(使用转移方程)

70.爬楼梯(简单)
746.使用最小花费爬楼梯 (简单)
62.不同路径:网格的左上角到右下角共有多少条不同的路径

63.不同路径 II:网格中的障碍物和空位置分别用 1 和 0 来表示。

在这里插入图片描述
要处理单行和单列的情况
在这里插入图片描述
如果单元格为阻碍物则不变,否则按前面的赋值。

var uniquePathsWithObstacles = function(obstacleGrid) {
    var dp = obstacleGrid.map(arr => arr.map(val => val===1? 0 : 1))
    // console.log(dp)
    var row = obstacleGrid.length, col = obstacleGrid[0].length
    for(let i = 1; i<row; i++){
        dp[i][0] && (dp[i][0] = dp[i-1][0])
    }
    for(let j = 1; j<col; j++){
        dp[0][j] && (dp[0][j]= dp[0][j-1])
    }
    for(let i=1; i<row; i++) {
        for(let j=1; j<col; j++) {
            if(dp[i][j]) {
                dp[i][j] = dp[i-1][j] + dp[i][j-1]
            }
        }
    }
    // console.log(dp)
    return dp[row-1][col-1]
};

343.整数拆分(中等)

给定一个正整数 n ,将其拆分为 k 个 正整数 的和( k >= 2 ),并使这些整数的乘积最大化。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

var integerBreak = function(n) {
    var dp = new Array(n+1).fill(0)
    dp[2] = 1 // 整数2开始才能拆分
    for(let i=3; i<=n; i++) {
        for(let j=1; j<=i-2; j++) {
            dp[i] = Math.max(dp[i], j * (i - j), j * dp[i - j])
        }
    }
    return dp[n]
};

96.不同的二叉搜索树 (中等)

做过的题目
dp[i] : i 个节点时的种类数

思想:
谁做根节点,遍历 J 作为根节点;
J 为根节点的种数谁决定,左子树数量 * 右子树数量;
遍历1—n个节点的种数。

var numTrees = function(n) {
    var dp = new Array(n+1).fill(0)
    dp[0] = 1
    for(let i=1; i<=n; i++) {
        for(let j=1; j<=i; j++) {
            dp[i] += dp[j-1] * dp[i-j]
        }
    }
    return dp[n]
};

动态规划——子序列问题

300.最长递增子序列(已做、入门)

dp[i]:第 i 个作为尾节点的最长递增序列长度。

var lengthOfLIS = function(nums) {
    var dp = new Array(nums.length).fill(1)
    for(let i=1; i<nums.length; i++) {
        for(let j=0; j<i; j++) {
            if(nums[j] < nums[i]) {
                dp[i] = Math.max(dp[i], dp[j] + 1)
            }
        }
    }
    return Math.max(...dp)
};

1143.最长公共子序列 (旧题)

dp[i][j]:字符串a前i个与字符串b前j个的最长公共子序列长度、

dp[i][j]:长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符串text2的最长公共子序列为dp[i][j]

var longestCommonSubsequence = function(text1, text2) {
    var row = text1.length, col = text2.length
    var dp = new Array(row+1).fill(0).map(val => new Array(col+1).fill(0))

    for(let i=1; i<=row; i++) {
        for(let j=1; j<=col; j++) {
            dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1])
            if(text2[j-1] == text1[i-1]){
                dp[i][j] = Math.max(dp[i][j], dp[i-1][j-1] + 1)
            }
        }
    }
    return dp[row][col]
};

1035.不相交的线 (中等)fail

在这里插入图片描述
在这里插入图片描述
这么分析完之后,大家可以发现:本题说是求绘制的最大连线数,其实就是求两个字符串的最长公共子序列的长度!

那么本题就和我们刚刚讲过的这道题目动态规划:1143.最长公共子序列 (opens new window)就是一样一样的了。
(卧槽)

674.最长连续递增序列 (简单)

与300.最长递增子序列最大的区别在于“连续”

dp[i]:以下标i为结尾的数组的连续递增的子序列长度为dp[i]。

718.最长重复子数组 (中等)

给两个整数数组 nums1 和 nums2 ,返回 两个数组中 公共的 、长度最长的子数组的长度 。

注意题目中说的子数组,其实就是连续子序列。

dp[i][j] :以下标i - 1为结尾的A,和以下标j - 1为结尾的B,最长重复子数组长度为dp[i][j]。

注意这里的dp含义也不同于1143.最长公共子序列,这里的子数组是包括 i-1 的。

var findLength = function(nums1, nums2) {
    var [row, col] = [nums1.length, nums2.length]
    var dp = new Array(row+1).fill(0).map(val => new Array(col+1).fill(0))
    var res = 0
    for(let i=1; i<=row; i++) {
        for(let j=1; j<=col; j++) {
            if(nums1[i-1] === nums2[j-1]) {
                dp[i][j] = dp[i-1][j-1] + 1
                res = Math.max(dp[i][j], res)
            }
        }
    }
    return res
};

53.最大子数组和 (已做、简单)

dp[i]:包括下标i之前的最大连续子序列和为dp[i]。
在这里插入图片描述

647.回文子串 (中等)

在这里插入图片描述

输入:s = "aaa"
输出:6
解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"

暴力:两层for循环,遍历区间起始位置和终止位置,然后判断这个区间是不是回文。

方法一:双指针法

首先确定回文串,就是找中心然后向两边扩散看是不是对称的就可以了。

在遍历中心点的时候,要注意中心点有两种情况。

一个元素可以作为中心点,两个元素也可以作为中心点。

var countSubstrings = function(s) {
    var len = s.length, res = 0
    function count(l, r) {
        let temp = 0
        while(l>=0 && r<len && s[l]==s[r]) {
            l--
            r++
            temp++
        }
        return temp
    }
    for(let i = 0; i<len; i++) {
        res += count(i, i)
        res += count(i, i+1)
    }
    return res
};

方法二:动态规划 (fail)

布尔类型的dp[i][j]:表示区间范围[i,j] (注意是左闭右闭)的子串是否是回文子串,如果是dp[i][j]为true,否则为false。

因为dp[i][j]的定义,所以j一定是大于等于i的,那么在填充dp[i][j]的时候一定是只填充右上半部分。

最难的是遍历顺序:
根据dp[i + 1][j - 1]是否为true,在对dp[i][j]进行赋值true的。dp[i + 1][j - 1] 在 dp[i][j]的左下角。

所以一定要从下到上,从左到右遍历,这样保证dp[i + 1][j - 1]都是经过计算的。

有的代码实现是优先遍历列,然后遍历行,其实也是一个道理,都是为了保证dp[i + 1][j - 1]都是经过计算的。
在这里插入图片描述

var countSubstrings = function(s) {
    var len = s.length, res = 0
    var dp = new Array(len).fill(0).map(val => new Array(len).fill(false))
    for(let i=len-1; i>=0; i--) {
        for(let j=i; j<=len-1; j++) {
            if(s[i] === s[j]) {
                if((j - i) <= 1) {
                    res++
                    dp[i][j] = true
                } else if(dp[i+1][j-1]) {
                    res++
                    dp[i][j] = true
                }
            }
        }
    }
    return res
};

516.最长回文子序列 (中等)fail

回文子串是要连续的,回文子序列可不是连续的! 回文子串,回文子序列都是动态规划经典题目。

思路其实是差不多的,但本题要比求回文子串简单一点,因为情况少了一点。
在这里插入图片描述
动态规划

dp[i][j]:字符串s在[i, j]范围内最长的回文子序列的长度为dp[i][j]。
在这里插入图片描述

var longestPalindromeSubseq = function(s) {
    var len = s.length
    var dp = new Array(len).fill(0).map(val => new Array(len).fill(0))
    for(let i=len-1; i>=0; i--) {
        for(let j=i; j<=len-1; j++) {
            if(s[i]===s[j]) {
                if((j-i)<=1) {
                    dp[i][j] = j - i + 1
                } else {
                    dp[i][j] = dp[i+1][j-1] + 2
                }
            } else {
                dp[i][j] = Math.max(dp[i][j-1], dp[i+1][j])
            }
        }
    }
    return dp[0][len-1]
};

5.最长回文子串(中等、已做-中心扩展算法)

在这里插入图片描述
中心扩展

function palindrome(s, l, r) {
    while (l >= 0 && r < s.length && s[l] == s.charAt(r)) {
        // 向两边展开
        l--; r++;
    }
    // 左闭右开
    return s.slice(l + 1, r);
}

var longestPalindrome = function(s) {
    let ans = ""
    for (let i=0; i<s.length; i++) {
        let str1 = palindrome(s, i, i)
        let str2 = palindrome(s, i, i+1)
        ans = ans.length < str1.length ? str1 : ans
        ans = ans.length < str2.length ? str2 : ans
    }
    return ans;
};

动态规划

var longestPalindrome = function(s) {
    var len = s.length, res = 0, temp = null
    var dp = new Array(len).fill(0).map(val => new Array(len).fill(false))
    for(let i=len-1; i>=0; i--) {
        for(let j=i; j<=len-1; j++) {
            if(s[i] === s[j]) {
                if((j - i) <= 1 || dp[i+1][j-1]) {
                    if(res < (j - i + 1)) {
                        res = j - i + 1
                        temp = s.slice(i, j+1)
                    }
                    dp[i][j] = true
                }
            }
        }
    }
    return temp
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值