LeetCode300.最長遞增子序列

Tomorrowland_D發表於2024-08-23

LeetCode300.最長遞增子序列

力扣題目連結(opens new window)

給你一個整數陣列 nums ,找到其中最長嚴格遞增子序列的長度。

子序列是由陣列派生而來的序列,刪除(或不刪除)陣列中的元素而不改變其餘元素的順序。例如,[3,6,2,7] 是陣列 [0,3,1,6,2,2,7] 的子序列。

示例 1:

  • 輸入:nums = [10,9,2,5,3,7,101,18]
  • 輸出:4
  • 解釋:最長遞增子序列是 [2,3,7,101],因此長度為 4 。

示例 2:

  • 輸入:nums = [0,1,0,3,2,3]
  • 輸出:4

示例 3:

  • 輸入:nums = [7,7,7,7,7,7,7]
  • 輸出:1

提示:

  • 1 <= nums.length <= 2500
  • -104 <= nums[i] <= 104

動態規劃思路講解

  • 我之前也講過一篇最長上升子序列的文章,也可以去看看,本文基於線性dp:最長上升子序列 - Tomorrowland_D - 部落格園 (cnblogs.com))來詳細講解這道題的思路

狀態變數以及其含義

  • 我們設定狀態變數dp[i],表示以nums[i]為結尾的最長上升子序列的長度
  • 我們舉個例子,以示例1為例,我們來推導一下為什麼可以用dp[i]來表示以nums[i]為結尾的最長上升子序列
  • nums陣列: [10,9,2,5,3,7,101,18]
  1. 以10結尾的最長上升子序為:[10]
  2. 以9為結尾的最長上升子序列為:[9]
  3. 以2為結尾的最長上升子序列為:[2]
  4. 以5為結尾的最長上升子序列為:[2,5]
  5. 以3為結尾的最長上升子序列為:[2,3]
  6. 以7為結尾的最長上升子序列為:[2,3,7]
  7. 以101為結尾的最長上升子序列為:[2,3,7,101]
  8. 以18為結尾的最長上升子序列為:[2,3,7,18]
  • 由上面的分析可知,以101為結尾的最長上升子序列是我們要求的最終的結果,並且這個結果的狀態可以由前面的狀態推出,因此我們設立dp[i]這個狀態變數表示以nums[i]為結尾的最長上升子序列。

遞推公式:

  • 我們可以設立兩個指標i,j來進行操作,i指標來遍歷nums的每一個元素,j指標來遍歷nums[i]之前的所有元素,由於我們要找出最大的上升子序列,所以說每個元素我們都要找到nums中在這個元素之前的所有比這個元素要小的元素,這樣才能儘可能的構成最大的遞增子序列。

  • 所以說我們使用i,j指標來遍歷字串。

  • nums[i]>nums[j]時,意味著我們當前元素大於之前的一個元素,這兩個元素之間可以構成一個遞增子序列,所以說我們可能要進行更新dp[i],為什麼是可能呢?因為我們dp[i]的值可能比dp[j]+1(dp[j]+1的意思就是前j個元素構成的遞增序列,再加上num[i]這個值的長度)這個值更大,所以說我們得取一個最大的值。

  • 因此,遞推公式為:

        vector<int> dp(nums.size(),1);
        int ans=1;
        for(int i=1;i<nums.size();i++){
            for(int j=0;j<i;j++){
                if(nums[i]>nums[j]) dp[i]=max(dp[i],dp[j]+1);
            }
            ans=max(ans,dp[i]);
        }

遍歷順序

  • 由於dp[i]是要由它之前的元素dp[j]來推導的,因此遍歷順序明顯是從前向後遍歷

如何初始化?

  • 首先,我們將dp[i]中的所有值全都初始化為1,因為每個元素至少都有一個遞增子序列(也就是它本身構成的子序列)
  • 然後,依據我們的遞推公式從前向後進行初始化操作即可。

舉例驗證dp陣列

  • nums陣列: [10,9,2,5,3,7,101,18]
  1. 以10結尾的最長上升子序為:[10]
  2. 以9為結尾的最長上升子序列為:[9]
  3. 以2為結尾的最長上升子序列為:[2]
  4. 以5為結尾的最長上升子序列為:[2,5]
  5. 以3為結尾的最長上升子序列為:[2,3]
  6. 以7為結尾的最長上升子序列為:[2,3,7]
  7. 以101為結尾的最長上升子序列為:[2,3,7,101]
  8. 以18為結尾的最長上升子序列為:[2,3,7,18]
  • 這個例子也說明了我們的dp陣列是正確的

程式碼實現

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        vector<int> dp(nums.size(),1);
        //這個初始值為1,因為至少都有長度為1的遞增子序列
        int ans=1;
        for(int i=1;i<nums.size();i++){
            for(int j=0;j<i;j++){
                if(nums[i]>nums[j]) dp[i]=max(dp[i],dp[j]+1);
            }
            ans=max(ans,dp[i]);
        }
        return ans;
    }
};

相關文章