Leetcode: 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?
讲解一: 用一个数组来存 the smallest tail of all increasing subsequences with length i+1
in tails[i]
.
For example, say we have nums = [4,5,6,3]
, then all the available increasing subsequences are:
len = 1 : [4], [5], [6], [3] => tails[0] = 3
len = 2 : [4, 5], [5, 6] => tails[1] = 5
len = 3 : [4, 5, 6] => tails[2] = 6
We can easily prove that tails is a increasing array. Therefore it is possible to do a binary search in tails array to find the one needs update.
Each time we only do one of the two:
(1) if x is larger than all tails, append it, increase the size by 1
(2) if tails[i-1] < x <= tails[i], update tails[i]
Doing so will maintain the tails invariant. The the final answer is just the size.
讲解二:
在1,3,5,2,8,4,6
这个例子中,当到6时,我们一共可以有四种
(1)不同长度
(2)且保证该升序序列在同长度升序序列中末尾最小
的升序序列
1
1,2
1,3,4
1,3,5,6
这些序列都是未来有可能成为最长序列的候选人。这样,每来一个新的数,我们便按照以下规则更新这些序列
-
如果
nums[i]
比所有序列的末尾都大,或等于最大末尾,说明有一个新的不同长度序列产生,我们把最长的序列复制一个,并加上这个nums[i]
。 -
如果在中间,则更新那个末尾数字刚刚大于等于自己的那个序列,说明那个长度的序列可以更新了。
比如这时,如果再来一个9,那就是第一种情况,更新序列为
1
1,2
1,3,4
1,3,5,6
1,3,5,6,9
如果再来一个3,那就是第二种情况,更新序列为
1
1,2
1,3,3
1,3,5,6
如果再来一个0,那就是第二种情况,更新序列为
0
1,2
1,3,3
1,3,5,6
前两种都很好处理,O(1)就能解决,主要是第三种情况,实际上我们观察直到6之前这四个不同长度的升序序列,他们末尾是递增的,所以可以用二分搜索来找到适合的更新位置。
1 public class Solution { 2 public int lengthOfLIS(int[] nums) { 3 if (nums==null || nums.length==0) return 0; 4 ArrayList<Integer> list = new ArrayList<Integer>(); 5 for (int num : nums) { 6 if (list.size()==0 || num>list.get(list.size()-1)) 7 list.add(num); 8 else { 9 update(list, num); 10 } 11 } 12 return list.size(); 13 } 14 15 public void update(ArrayList<Integer> list, int num) { 16 int l=0, r=list.size()-1; 17 while (l <= r) { 18 int m = l + (r-l)/2; 19 if (list.get(m) == num) return; 20 else if (list.get(m) > num) { 21 r = m-1; 22 } 23 else l = m+1; 24 } 25 list.set(l, num); 26 } 27 }
时间O(N^2), 空间O(N)
DP
1 public class Solution { 2 public int lengthOfLIS(int[] nums) { 3 if (nums==null || nums.length==0) return 0; 4 int res = 1; 5 int len = nums.length; 6 int[] dp = new int[len]; 7 Arrays.fill(dp, 1); 8 for (int i=1; i<len; i++) { 9 for (int j=0; j<i; j++) { 10 if (nums[j] < nums[i]) { 11 dp[i] = Math.max(dp[i], dp[j]+1); 12 if (dp[i] > res) res = dp[i]; 13 } 14 } 15 } 16 return res; 17 } 18 }
follow up:怎么求真正的Longest Increasing Subsequence呢?
我现在的想法是在DP基础上,另外构建一个ArrayList<ArrayList<Integer>>存以nums[i]为结尾的LIS