最长递增子序列

DESC1:

给你一个整数数组 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
    -104 <= nums[i] <= 104

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-increasing-subsequence
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

CODE1:

JAVA:

class Solution {
    public int lengthOfLIS(int[] nums) {
        if (nums == null || nums.length==0) {
            return 0;
        }
        int res=1;
        int n = nums.length;
        int[] dp = new int[n];
        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] = Math.max(dp[i], dp[j]+1);
                }
            }
            res = Math.max(res, dp[i]);
        }
        return res;
    }
}

Python:

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        if not nums or len(nums)==0:
            return 0
        
        dp = [1] * len(nums)
        res = 1
        for i in range(len(nums)):
            for j in range(i):
                if nums[i] > nums[j]:
                    dp[i] = max(dp[i], dp[j] + 1)
            res = max(res, dp[i])
        return res

NOTES1:

  1. 动态规划,dp代表以位置i为末尾的最大序列长度,状态转移为:先初始化为dp[i]=1,然后遍历i前的各元素进行对比,如果大于其,说明可以作为新序列结点,递增序列可以加1,满足条件的可能有多个,不断更新dp[i]取最大值。最后根据dp不断更新最终结果。
  2. 复杂度分析

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

        空间复杂度:O(n)。

CODE2:

class Solution {
    public int lengthOfLIS(int[] nums) {
        if (nums == null || nums.length==0) {
            return 0;
        }
        int len=1;
        int n = nums.length;
        int[] dp = new int[n+1];
        dp[1] = nums[0];
        for (int i=1; i<n; i++) {
            if (nums[i] > dp[len]) {
                dp[++len] = nums[i];
            } else {
                int left=1, right=len, pos=0;
                while (left <= right) {
                    int mid = left + (right-left)/2;
                    if (dp[mid]<nums[i]) {
                        pos=mid;
                        left = mid+1;
                    } else {
                        right = mid-1;
                    }
                }
                dp[pos+1] = nums[i];
            }
        }
        return len;
    }
}

NOTES2:

  1. 贪心 + 二分查找

  2. 考虑一个简单的贪心,如果我们要使上升子序列尽可能的长,则我们需要让序列上升得尽可能慢,因此我们希望每次在上升子序列最后加上的那个数尽可能的小。

  3. dp[i]=x表示子序列长度len=i时,序列最后一元素的最小值,如【1,2,4】,dp[3]=4

  4. 我们要让没一个序列长度的节点元素尽可能的小,才能为后面更长的序列长度创造条件,如【1,2,4,5】,来了一个元素3,因小于5,所以不能增加序列长度,但我们用二分法去查找dp中的所有尾结点元素,去看有没有比他大的元素,用它去更新最邻近最小元素的位置,此处为4,即更新完为【1,2,3,5】。注意pos默认值和记录位置的含义,及什么时候去更新pos, 最后要放在pos+1位置,即最后mid的后一位。

  5. 时间复杂度:O(nlog⁡n)。

    空间复杂度:O(n)。

DESC3:

题目描述

给定数组arr,设长度为n,输出arr的最长递增子序列。(如果有多个答案,请输出其中字典序最小的)

示例1

输入

[2,1,5,3,6,4,8,9,7]

返回值

[1,3,4,8,9]

示例2

输入

[1,2,8,6,4]

返回值

[1,2,4]

说明

其最长递增子序列有3个,(1,2,8)、(1,2,6)、(1,2,4)其中第三个字典序最小,故答案为(1,2,4)

备注:

n≤105n \leq 10^5n≤105
1≤arri≤1091 \leq arr_i \leq 10^91≤arri​≤10^9

CODE3:

JAVA:

import java.util.*;


public class Solution {
    /**
     * retrun the longest increasing subsequence
     * @param arr int整型一维数组 the array
     * @return int整型一维数组
     */
    public int[] LIS (int[] arr) {
        // write code here
        if (arr == null || arr.length == 0) {
            return arr;
        }
        int len=1;
        int n = arr.length;
        int[] dp = new int[n+1];
        int[] index = new int[n];
        dp[1] = arr[0];
        for (int i=0; i<n; i++) {
            if (arr[i] > dp[len]) {
                dp[++len] = arr[i];
                index[i] = len; 
            } else {
                int l=1, r=len;
                while (l<=r) {
                    int mid = l+(r-l)/2;
                    if (arr[i]>dp[mid]) {
                        l = mid+1;
                    } else {
                        r = mid-1;
                    }
                }
                dp[l] = arr[i];
                index[i] = l;
            }
        }
        int[] res = new int[len];
        for (int i=n-1;i>=0;i--) {
            if (index[i] == len) {
                res[--len] = arr[i];
            }
        }
        return res;
    }
    
}

NOTES3:

  1. 两步走,第一步基于code2思路先寻找最长递增子序列,第二步,增加一个辅助数组,大小和输入数组长度相同,记录数组各个元素被放在了最长序列数组dp的位置;如数组arr【1,2,3,5,4】对应的dp=[0,1,2,3,4,0],最长序列长度为4,对应的index数组[1,2,3,4,4],即最后一个4因为更新了5,所以也放在了4;
  2. 根据index进行遍历,递减从序列最后判断获取符合条件对应的元素;注意len是递减的,所以会排除掉第二个索引4,也即对应的原始5;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值