[路飞]-最长递增子序列leetcode300

565 阅读1分钟

「这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战

首先,来个开胃菜。给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。

nums = [1,3,5,4,7]  //  最长连续递增序列是 [1,3,5], 长度为3
  • 我们首先分析连续递增i> 0 && nums[i-1] <= nums[i] // 开始位置要大于0 当前比上一位要大或相等构成连续递增

  • 然后记录这个时候的长度 i-start + 1, 我们要保存临时长度ans, 和之前的长度对比取最大Math.max(ans, i-start+1), 遍历结束后就是我们的最大长度。

//完 整代码
let start = 0
let ans = 0
for(let i = 0; i< nums.length; i++){
    if(i>0 && nums[i]<= nums[i-1]){
        start = i
    }
    ans = Math.max(ans, i-start +1)
}
return ans

现在我们进入leetcode300题的解析。 给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

  • 最长严格递增只能是比前面的大,相等的不算

  • 我们使用动态规划解题

image.png

  • 首先初始化一个dp数组每个元素为1
  • 外层循环用一个临时变量max,取Math.max(dp[i], max)
  • 然后从索引i-1的位置往前比较 nums[i] > nums[j] 计算长度保存到dp[i] = Math.max(dp[i],dp[j] + 1)当前位置
 //完 整代码
 //判断边界
const n = nums.length 
if(n <= 1){
    return n
}
let max = 0
const dp = new Array(n).fill(1) // 初始化
for(let i = 1; i < n; i++){
    for(let j = i-1; j>=0; j--){
        // 严格递增
        if(nums[i] > nums[j]){
            //  获取dp[i]的最大值  dp[j] + 1 长度包含nums[i]当前位置所以+1
            dp[i] = Math.max(dp[i], dp[j] + 1)
        }
    }
    max = Math.max(d[i], max)
}
return max

上述解法中,当前元素都和之前的元素做了对比,如果做个记录,就不需要多余的比较

  • 优化解法,定义一个dp[i]记录一个长度为i最长递增子序列尾部元素的最小值
  • dp[1]初始化 nums[0]
  • 如果dp里的元素都比nums[i],将其插入到最后
  • 否则二分查找dp,用num[i]覆盖掉比它大的元素

image.png

const n = nums.length;
let max = 1
if(n <=1 )return n
const dp = [null, nums[0]]
for(let i = 1; i< n; i++){
    if(dp[max] < nums[i]){
        dp[++max] = nums[i]
    }
    // 二分查找
    let left = 1,
    right = max,
    mid,
    pos = 0
    while(left <= right){
        mid = (right+left )>>1
        if(dp[mid] < nums[i]){
            //在左边
            left = mid+1
            pos = mid
        }else{
            right = mid-1
        }     
    }
    // 替换掉比当前元素大的那个
    dp[pos+1] = nums[i]
}
return max

总结:使用dp 记录一个长度为i最长递增子序列尾部元素的最小值 ,可以减少对比次数,优化了时间复杂度 O(nlogn)