Problem
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?
Credits:
Special thanks to @pbrother for adding this problem and creating all test cases.
Solution 1
很经典的DP问题,用dp[i]来表示以nums[i]为结尾的最长子序列,那么
1. 当nums[i] 比前面任意一个数nums [ j ] ( j 的范围 [ 0 , i - 1 ] )都大时,dp[ i ] = 1;
2. 否则,就是存在某个数 nums[ j ] < nums[ i ], dp[ i ] 就有可能是dp [ j ] 的长度再加上 1 ,就是 nums[i] 自己 , dp[ i ] = max ( dp[ j ] + 1 ) , j : [ 0 , i -1 ] inclusive;
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
if(nums.empty()) return 0;
const int N = nums.size();
vector<int> dp(N , 1);
int rst = 1;
for( int i = 1; i < N; i++){
for( int j = 0; j < i; j++){
if(nums[i] > nums[j]){
dp[i] = max(dp[i], dp[j] + 1);
}
}
rst = max(rst, dp[i]);
}
return rst;
//return dp[N-1]; <span style="color:#ff0000;">buggy 结果应该是dp [0, N-1] 的最大值,所以上面用了变量rst 来纪录。</span>
}
};
Solution 2
还可以用用binary search 解决。
关键是要想到用一个数组 tails 来记录一个最长递增子序列, 这样从头遍历数组nums时,
1. 如果 num > tails.back(), 把 num 扔到 tails尾巴就好
2. 否则,就要从tails 里找到第一个比num大的数 (这里用二分查找实现,就是 findFirstBiggerN () 做的事),然后用 num 取代刚才找到的数
最后 tails 的长度,就是要找的结果。
step 1. 很好理解 ;
step 2. 就是尽量把每个数都最优的 fit 到tails 里,让tails里面的数尽可能的小,才能找到最长递增子序列。
class Solution {
int findFirstBiggerN( const vector<int>& nums, int n){
int left = 0, right = nums.size() - 1;
while( left <= right ){
int mid = (left + right) / 2;
if(nums[mid] == n) return mid;
if( nums[mid] > n) {
right = mid - 1;
}
else {
left = mid + 1;
}
}
return left;
}
public:
int lengthOfLIS(vector<int>& nums) {
vector<int> tails;
for( int num : nums){
if(tails.empty() ||num > tails.back()) {
tails.push_back(num);
}
else{
int idx = findFirstBiggerN(tails, num);
tails[idx] = num ;
}
}
return tails.size();
}
};