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:
- 动态规划,dp代表以位置i为末尾的最大序列长度,状态转移为:先初始化为dp[i]=1,然后遍历i前的各元素进行对比,如果大于其,说明可以作为新序列结点,递增序列可以加1,满足条件的可能有多个,不断更新dp[i]取最大值。最后根据dp不断更新最终结果。
复杂度分析
时间复杂度: 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:
贪心 + 二分查找
考虑一个简单的贪心,如果我们要使上升子序列尽可能的长,则我们需要让序列上升得尽可能慢,因此我们希望每次在上升子序列最后加上的那个数尽可能的小。
dp[i]=x表示子序列长度len=i时,序列最后一元素的最小值,如【1,2,4】,dp[3]=4
我们要让没一个序列长度的节点元素尽可能的小,才能为后面更长的序列长度创造条件,如【1,2,4,5】,来了一个元素3,因小于5,所以不能增加序列长度,但我们用二分法去查找dp中的所有尾结点元素,去看有没有比他大的元素,用它去更新最邻近最小元素的位置,此处为4,即更新完为【1,2,3,5】。注意pos默认值和记录位置的含义,及什么时候去更新pos, 最后要放在pos+1位置,即最后mid的后一位。
时间复杂度:O(nlogn)。
空间复杂度: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:
- 两步走,第一步基于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;
- 根据index进行遍历,递减从序列最后判断获取符合条件对应的元素;注意len是递减的,所以会排除掉第二个索引4,也即对应的原始5;