LeetCode --- 300. 最长上升子序列

给定一个无序的整数序列,求出它最长上升子序列的长度(要求严格上升)
比如 [10,2,2,5,1,7,101,18] 的最长上升子序列是 [2,5,7,101]、[2,5,7,18],长度是4

  • 最长上升子序列(最长递增子序列,Longest Increasing Subsequence,LIS)

解法1 - 动态规划

状态定义
  • 假设数组是nums,[10,2,2,5,1,7,101,18]
    dp(i)是以nums[i]结尾的最长上升子序列的长度,i ∈ [0,nums.length)
  1. 以nums[0] 10结尾的最长上升子序列是10,所以dp(0) = 1
  2. 以nums[1] 2结尾的最长上升子序列是2,所以dp(1) = 1
  3. 以nums[2] 2结尾的最长上升子序列是2,所以dp(2) = 1
  4. 以nums[3] 5结尾的最长上升子序列是2、5,所以dp(3) = dp(1) + 1 = dp(2) + 1 = 2
  5. 以nums[4] 1结尾的最长上升子序列是1,所以dp(4) = 1
  6. 以nums[5] 7结尾的最长上升子序列是2、5、7,所以dp(5) = dp(3) + 1 = 3
  7. 以nums[6] 101结尾的最长上升子序列是2、5、7、101,所以dp(6) = dp(5) + 1 = 4
  8. 以nums[7] 18结尾的最长上升子序列是2、5、7、18,所以dp(7) = dp(5) + 1 = 4
  • 最长上升子序列的长度是所有dp(i)中最大值max { dp(i) }, i ∈ [0,nums.length)
状态转义方程
  • 遍历 j ∈ [0,i)
  • 当nums[i] > nums[j]
    nums[i]可以接在nums[j]后面,行程一个比dp(j)更长的上升子序列,长度为dp(j) + 1
    dp(i) = max { dp(i), dp(j) + 1 }
  • 当nums[i] <= nums[j]
    nums[i]不能接在nums[j]后面,跳过此次遍历(continue)
  • 状态的初始值
    dp(0) = 1
    所有的 dp(i) 默认都初始化为1
实现
int lengthOfLIS(int[] nums) {
    if (nums == null || nums.length == 0) return 0;
    int[] dp = new int[nums.length];
    int max = dp[0] = 1;
    for (int i = 1; i < dp.length; i++) {
        dp[i] = 1;
        for (int j = 0; j < i; j++ ) {
            if (nums[i] <= nums[j]) continue;
            dp[i] = Math.max(dp[i], dp[j] + 1);
        }
        max = Math.max(dp[i], max);
    }
    return max;
}
  • 时间复杂度:O(n),时间复杂度:O(n^2)

解法2 - 二分搜索

在这里插入图片描述

  • 把每个数字看作是一张扑克牌,从左到右按顺序处理每一张扑克牌
    将它压在(从左边数过来)第一个牌顶>=它的排队上面
    如果找不到牌顶>=它的牌堆,就在左右边新建一个牌堆,将它放入这个新牌堆中
    在这里插入图片描述
  • 当处理完所有牌,最终牌堆的数量就是最长上升子序列的长度
  • 思路(假设数组是nums,也就是最初的牌数组)
    top[i] 是第i个牌堆的牌顶,len是牌堆的数量,初始值为0
    遍历每一张牌num
  1. 利用二分搜索找出num最终要放入的牌堆位置index
  2. num作为第index个牌堆的牌顶,top[index] = num
  3. 如果index等于len,相当于新建一个牌堆,牌堆数量 +1,也就是len++
实现
int lengthOfLIS(int[] nums) {
    if (nums == null || nums.length == 0) return 0;
    int[] top = new int[nums.length];
    int len = 0;
    for (int num : nums) {
        int begin = 0, end = 0;
        while (begin < end) {
            int mid = (begin + end) >> 1;
            if (num <= top[mid]) {
                end = mid;
            } else {
                begin = mid + 1;
            }
        }
        top[begin] = num;
        if (begin == len) len++;
    }
    return len;
}
  • 空间复杂度:O(n),时间复杂度O(nlogn)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值