[LeetCode] 300. Longest Increasing Subsequence 最长递增子序列
Given an unsorted array of integers, find the length of longest increasing subsequence.
For example,
Given [10, 9, 2, 5, 3, 7, 101, 18]
,
The longest increasing subsequence is [2, 3, 7, 101]
, therefore the length is 4
. Note that there may be more than one LIS combination, it is only necessary for you to return the length.
Your algorithm should run in O(n2) complexity.
Follow up: Could you improve it to O(n log n) time complexity?
解法1: 动态规划DP,类似brute force的解法,维护一个一维dp数组,其中dp[i]表示以nums[i]为结尾的最长递增子串的长度。Time: O(n^2)
设长度为N的数组为{a0,a1, a2, ...an-1),则假定以aj结尾的数组序列的最长递增子序列长度为L(j),则L(j)={ max(L(i))+1, i<j且a[i]<a[j] }。也就是说,需要遍历在j之前的所有位置i(从0到j-1),找出满足条件a[i]<a[j]的L(i),求出max(L(i))+1即为L(j)的值。最后,我们遍历所有的L(j)(从0到N-1),找出最大值即为最大递增子序列。时间复杂度为O(N^2)。
例如给定的数组为{5,6,7,1,2,8},则L(0)=1, L(1)=2, L(2)=3, L(3)=1, L(4)=2, L(5)=4。所以该数组最长递增子序列长度为4,序列为{5,6,7,8}。
解法2: 二分查找法Binary Search, 维护一个单调递增子序列,如果当前值小于单调递增子序列中的某个元素,则替换之,因为单调递增子序列能否增长,值取决于最后一个元素,替换内部的元素并不影响。Time: O(nlogn)
假设存在一个序列d[1..9] = { 2,1 ,5 ,3 ,6,4, 8 ,9, 7},可以看出来它的LIS长度为5。
定义一个序列B,然后让 i = 1 to 9 逐个考察这个序列。用一个变量Len来记录现在最长LIS长度。
首先,把d[1]有序地放到B里,令B[1] = 2,就是说当只有1一个数字2的时候,长度为1的LIS的最小末尾是2。这时Len=1
然后,把d[2]有序地放到B里,令B[1] = 1,就是说长度为1的LIS的最小末尾是1,d[1]=2已经没用了,很容易理解吧。这时Len=1
接着,d[3] = 5,d[3]>B[1],所以令B[1+1]=B[2]=d[3]=5,就是说长度为2的LIS的最小末尾是5,很容易理解吧。这时候B[1..2] = 1, 5,Len=2
再来,d[4] = 3,它正好加在1,5之间,放在1的位置显然不合适,因为1小于3,长度为1的LIS最小末尾应该是1,这样很容易推知,长度为2的LIS最小末尾是3,于是可以把5淘汰掉,这时候B[1..2] = 1, 3,Len = 2
继续,d[5] = 6,它在3后面,因为B[2] = 3, 而6在3后面,于是很容易可以推知B[3] = 6, 这时B[1..3] = 1, 3, 6,还是很容易理解吧? Len = 3 了噢。
第6个, d[6] = 4,你看它在3和6之间,于是我们就可以把6替换掉,得到B[3] = 4。B[1..3] = 1, 3, 4, Len继续等于3
第7个, d[7] = 8,它很大,比4大,嗯。于是B[4] = 8。Len变成4了
第8个, d[8] = 9,得到B[5] = 9,嗯。Len继续增大,到5了。
最后一个, d[9] = 7,它在B[3] = 4和B[4] = 8之间,所以我们知道,最新的B[4] =7,B[1..5] = 1, 3, 4, 7, 9,Len = 5。
于是我们知道了LIS的长度为5。
注意,这个1,3,4,7,9不是LIS,它只是存储的对应长度LIS的最小末尾。有了这个末尾,我们就可以一个一个地插入数据。虽然最后一个d[9] = 7更新进去对于这组数据没有什么意义,但是如果后面再出现两个数字 8 和 9,那么就可以把8更新到d[5], 9更新到d[6],得出LIS的长度为6。
然后发现一件事情:在B中插入数据是有序的,而且是进行替换而不需要挪动——也就是说,可以使用二分查找,将每一个数字的插入时间优化到O(logN)~~~~~于是算法的时间复杂度就降低到了O(NlogN)~!
Java: DP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class Solution { public int lengthOfLIS( int [] nums) { if (nums == null || nums.length == 0 ) return 0 ; int max = 1 ; int [] lens = new int [nums.length]; Arrays.fill(lens, 1 ); for ( int i= 1 ; i<nums.length; i++) { for ( int j= 0 ; j<i; j++) { if (nums[j]<nums[i]) lens[i] = Math.max(lens[i], lens[j]+ 1 ); } max = Math.max(max, lens[i]); } return max; } } |
Java: BS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public class Solution { public int lengthOfLIS( int [] nums) { int [] increasing = new int [nums.length]; int size = 0 ; for ( int i= 0 ; i<nums.length; i++) { int left= 0 , right=size- 1 ; while (left<=right) { int m=(left+right)/ 2 ; if (nums[i] > increasing[m]) left = m + 1 ; else right = m - 1 ; } increasing[left] = nums[i]; if (left==size) size ++; } return size; } } |
Java: TreeSet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public class Solution { public int lengthOfLIS( int [] nums) { if (nums == null || nums.length == 0 ) return 0 ; int max = 1 ; TreeSet<Integer> ts = new TreeSet<>( new Comparator<Integer>() { @Override public int compare(Integer i1, Integer i2) { return Integer.compare(nums[i1], nums[i2]); } }); int [] lens = new int [nums.length]; Arrays.fill(lens, 1 ); for ( int i= 0 ; i<nums.length; i++) { if (ts.contains(i)) ts.remove(i); ts.add(i); Set<Integer> heads = ts.headSet(i); for ( int head: heads) { lens[i] = Math.max(lens[i], lens[head] + 1 ); } max = Math.max(max, lens[i]); } return max; } } |
Python: BS, T: O(nlogn), S: O(n)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | class Solution( object ): def lengthOfLIS( self , nums): LIS = [] def insert(target): left, right = 0 , len (LIS) - 1 # Find the first index "left" which satisfies LIS[left] >= target while left < = right: mid = left + (right - left) / 2 if LIS[mid] > = target: right = mid - 1 else : left = mid + 1 # If not found, append the target. if left = = len (LIS): LIS.append(target); else : LIS[left] = target for num in nums: insert(num) return len (LIS) |
Python: DP, T: O(n^2), S: O(n)
1 2 3 4 5 6 7 8 9 10 | class Solution( object ): def lengthOfLIS( self , nums): dp = [] # dp[i]: the length of LIS ends with nums[i] for i in xrange ( len (nums)): dp.append( 1 ) for j in xrange (i): if nums[j] < nums[i]: dp[i] = max (dp[i], dp[j] + 1 ) return max (dp) if dp else 0 |
Python: wo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class Solution( object ): def lengthOfLIS( self , nums): """ :type nums: List[int] :rtype: int """ if not nums: return 0 n = len (nums) dp = [ 1 ] * n max_len = 1 for i in xrange (n): for j in xrange (i): if nums[i] > nums[j]: dp[i] = max (dp[i], dp[j] + 1 ) max_len = max (max_len, dp[i]) return max_len |
C++:DP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | class Solution { public : int lengthOfLIS(vector< int >& nums) { if (nums.size() == 0) return 0; vector< int > dp(nums.size(), 1); int res = 1; for ( int i = 1; i < nums.size(); ++i){ for ( int j = 0; j < i; ++j){ if (nums[j] < nums[i]){ dp[i] = max(dp[i], 1+dp[j]); } } res = max(res, dp[i]); } return res; } }; |
C++:BS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | class Solution { public : int lengthOfLIS(vector< int >& nums) { if (nums.empty()) return 0; vector< int > ends{nums[0]}; for ( auto a : nums) { if (a < ends[0]) ends[0] = a; else if (a > ends.back()) ends.push_back(a); else { int left = 0, right = ends.size(); while (left < right) { int mid = left + (right - left) / 2; if (ends[mid] < a) left = mid + 1; else right = mid; } ends[right] = a; } } return ends.size(); } }; |
C++:BS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class Solution { public : int lengthOfLIS(vector< int >& nums) { vector< int > dp; for ( int i = 0; i < nums.size(); ++i){ int lo = 0, hi = dp.size(); while (lo < hi){ int mi = (lo + hi)/2; if (dp[mi] < nums[i]) lo = mi + 1; else hi = mi; } if (hi == dp.size()) dp.push_back(nums[i]); else dp[hi] = nums[i]; } return dp.size(); } }; |
类似题目:
[LeetCode] 354. Russian Doll Envelopes 俄罗斯套娃信封
[LeetCode] 673. Number of Longest Increasing Subsequence 最长递增序列的个数
[LeetCode] 674. Longest Continuous Increasing Subsequence 最长连续递增序列
All LeetCode Questions List 题目汇总
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core GC计划阶段(plan_phase)底层原理浅谈
· .NET开发智能桌面机器人:用.NET IoT库编写驱动控制两个屏幕
· 用纯.NET开发并制作一个智能桌面机器人:从.NET IoT入门开始
· 一个超经典 WinForm,WPF 卡死问题的终极反思
· ASP.NET Core - 日志记录系统(二)
· 博客园 & 1Panel 联合终身会员上线
· 支付宝事故这事儿,凭什么又是程序员背锅?有没有可能是这样的...
· https证书一键自动续期,帮你解放90天限制
· 在 ASP.NET Core WebAPI如何实现版本控制?
· 告别虚拟机!WSL2安装配置教程!!!