LeetCode:300. 最长递增子序列 动态规划O(n*n),二叉查找O(n*log(n))

300. 最长递增子序列

Leetcode 300. 最长递增子序列

题目描述

给定一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

示例 1

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。

示例 2

输入:nums = [0,1,0,3,2,3]
输出:4

示例 3

输入:nums = [7,7,7,7,7,7,7]
输出:1

提示

  • 1 <= nums.length <= 2500
  • -10^4 <= nums[i] <= 10^4

题目解析

解法1 - 动态规划

这道题目是一道经典的动态规划问题,我们可以用动态规划来解决。

dp[i] 表示以 nums[i] 结尾的最长递增子序列的长度。

那么,dp[i] 的值可以由 dp[j] 推导出来,其中 0 <= j < i

如果 nums[j] < nums[i] ,那么 dp[i] 就可以由 dp[j] 推导出来,因为 nums[j] 一定是 nums[i] 的前驱,所以 dp[i] 一定大于等于 dp[j] 加 1。

状态转移方程:

if nums[j] < nums[i]
    dp[i] = max(dp[j] + 1) ,0 <= j < i

最后,我们可以返回 dp 数组中的最大值,即为最长递增子序列的长度。

复杂度分析:

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2),其中 n n n 是数组的长度。
  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组的长度。

解法2 - 二分查找

我们可以用二分查找来优化解法1。

我们可以维护一个数组 tails,表示当前最长的递增子序列。

遍历数组 nums,对于每个 nums[i],如果nums[i]大于tails中的最大值,我们可以将nums[i]加入到tails中。
否则,我们可以用二分查找来找到 tails 中第一个大于等于 nums[i] 的元素 tail,并使用nums[i]来替换掉tail。

最后,tails 中的元素个数即为最长递增子序列的长度。

其二分法的核心思想是:插入数据替换比他大的最小的那个,保证这个队列始终是在保证最长的情况下,值最小的那个。

复杂度分析:

  • 时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn),其中 n n n 是数组的长度。
  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组的长度。

代码实现

解法1 - 动态规划

Go版本:

func lengthOfLIS(nums []int) int {
    n:=len(nums)
    dp:=make([]int,n)
    res:=1
    dp[0]=1
    for i:=1;i<n;i++{
        dp[i]=1
        for j:=0;j<i;j++{
            if(nums[i]>nums[j]){
                dp[i]=max(dp[i],dp[j]+1)
            }
        }
        res=max(res,dp[i])
    }
    return res
}

C++版本:

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
         int n=nums.size();
         if(n==1){
            return 1;
         }
         vector<int> dp(n,0);
         int res=0;
         dp[0]=1;
         for(int i=1;i<n;i++){
             dp[i]=1;
             for(int j=0;j<i;j++){
               if(nums[i]>nums[j]){
                  dp[i]=max(dp[i],dp[j]+1);
                }
             }
             res=max(res,dp[i]);
         }
         return res;
    }
};

解法2 - 二分查找

Python版本:

class Solution(object):
    def lengthOfLIS(self, nums):
        n = len(nums)
        if n==1:
            return 1
        tails = [nums[0]]
        for i in range(n):
            if nums[i]>tails[-1]:
                tails.append(nums[i])
                continue
            
            l,r = 0,len(tails)-1
            while l<r:
                mid = l + (r - l) // 2
                if tails[mid]<nums[i]:
                    l = mid + 1
                else:
                    r = mid
            tails[l] = nums[i]
        return len(tails)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值