【Leetcode 每日一題】300.最長上升子序列

題目描述

給定一個無序的整數數組,找到其中最長上升子序列的長度。

示例:

輸入: [10,9,2,5,3,7,101,18]
輸出: 4 
解釋: 最長的上升子序列是 [2,3,7,101],它的長度是 4。

說明:

可能會有多種最長上升子序列的組合,你只需要輸出對應的長度即可。
你算法的時間複雜度應該爲 O(n2) 。
進階: 你能將算法的時間複雜度降低到 O(n log n) 嗎?

思路一——動態規劃

動態規劃的題難點就在於狀態轉移方程dp[i]=max(dp[j])+1,其中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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章