題目描述
給定一個無序的整數數組,找到其中最長上升子序列的長度。
示例:
輸入: [10,9,2,5,3,7,101,18]
輸出: 4
解釋: 最長的上升子序列是 [2,3,7,101],它的長度是 4。
說明:
可能會有多種最長上升子序列的組合,你只需要輸出對應的長度即可。
你算法的時間複雜度應該爲 O(n2) 。
進階: 你能將算法的時間複雜度降低到 O(n log n) 嗎?
思路一——動態規劃
動態規劃的題難點就在於狀態轉移方程,其中0≤j<i且num[j]<num[i]。
那麼,看到轉移方程可能你已經知道思路了。逐個判斷數組中每個元素與它之前的所有元素大小,若大於之前的元素,比較當前遞增長度與之前的元素遞增長度加一,取最大值。
最後,對dp數組排序,最大值即爲最長上升子序列長度。
- 時間複雜度:O(n^2)
- 空間複雜度:O(n)
int lengthOfLIS(vector<int>& nums) {
if(!nums.size())
return 0;
vector<int>dp(nums.size(),1);
for(int i=0;i<nums.size();i++)
{
for(int j=0;j<i;j++)
{
if(nums[i]>nums[j])
dp[i] = max(dp[i],dp[j]+1);
}
}
sort(dp.begin(),dp.end(),greater<int>());
return dp[0];
}
思路二——貪心 + 二分查找
貪心:我們想要上升子序列儘可能的長,那麼就需要讓上升子序列儘可能的慢,因此我們希望每次在上升子序列最後加上的那個數儘可能的小。eg:對於測試用例[10,9,2,5,3,7,101,18],最後得到的最長子序列是[2,3,7,18]而不是其他的序列[2,5,7,18]、[2,3,7,101]。
那麼我們需要構造一個數組d[i] ,表示長度爲 i 的最長上升子序列的末尾元素的最小值,用len記錄目前最長上升子序列的長度,起始時len爲1,d[1] = nums[0]。
從前向後遍歷nums數組,當nums[i]>d[len]時,則直接加入到 d 數組末尾,並更新 len=len+1;否則,在 d 數組中二分查找,找到第一個比 nums[i] 小的數 d[k] ,並更新 d[k + 1] = nums[i],若找不到,說明所有的數都比 nums[i] 大,因而直接更新 d[0]。
- 時間複雜度:O(nlog(n))
- 空間複雜度:O(n)
int lengthOfLIS(vector<int>& nums) {
if (!nums.size()) return 0;
vector<int>dp(nums.size() + 1, 0);//+1防止nums只有一個元素時,dp[1]越界
int len = 1;
dp[1] = nums[0];
for (int i = 1; i < nums.size(); 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;//繼續找,找到第一個比nums[i]小的爲止
}
else
right = mid - 1;
}
dp[pos + 1] = nums[i];
}
}
return len;
}