1. 程式人生 > >LeetCode刷題Medium篇Longest Increasing Subsequence

LeetCode刷題Medium篇Longest Increasing Subsequence

題目

Given an unsorted array of integers, find the length of longest increasing subsequence.

Example:

Input: [10,9,2,5,3,7,101,18]
Output: 4 
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4. 

Note:

  • 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?

十分鐘嘗試

很明顯是個動態規劃問題,設計陣列,儲存當前元素為止增加的序列長度,求出結果後發現怎麼都不對,原來題目沒有要求必需連續,我寫的演算法是求連續增加子序列最大長度,另外最初我用的二維陣列,一直以為需要二維陣列,其實一緯陣列就可以。二維結果也一樣。先貼出這個題目的解法,如下:

class Solution {
    public int lengthOfLIS(int[] nums) {
        if(nums.length==0) return 0;
        if(nums.length==1) return 1;
        int[][] dp=new int[nums.length][nums.length];
        for(int k=0;k<nums.length;k++){
            dp[k][0]=1;
        }
        int max=1;
        for(int i=0;i<nums.length;i++){
            for(int j=1;j<nums.length;j++){
                if(nums[j]>nums[j-1]){
                    dp[i][j]=dp[i][j-1]+1;
                }
                else{
                    dp[i][j]=1;
                }
                 max=Math.max(max,dp[i][j]);
            }
     
        }
        return max;
        
    }
}

改成一緯陣列,程式碼如下:

package com.puhui.goosecard.manage;

/**
 * 2 * @Author: kerry
 * 3 * @Date: 2018/12/7 10:18
 * 4
 */
public class Test {

    public static int lengthOfLIS(int[] nums) {
        if (nums.length == 0) {
            return 0;
        }
        if (nums.length == 1) {
            return 1;
        }
        int[] dp = new int[nums.length];
        dp[0] = 1;
        int max = 1;
        for (int i = 1; i < nums.length; i++) {
            if (nums[i] > nums[i - 1]) {
                dp[i] = dp[i - 1] + 1;
            } else {
                //修改第一版,如果小於dp[i][j]為1,截止當前元素自增序列為1
                dp[i] = 1;
            }
            max = Math.max(max, dp[i]);
        }
        return max;

    }

    public static void main(String[] args) {
        int[] nums = {10, 9, 2, 5, 3, 7, 101, 18};
        System.out.println(lengthOfLIS(nums));

    }
}

回到這個題目,這是我第一次用dp解決出來問題,為自己喝彩,雖然題目理解有偏差,但是這個題目應該也是dp問題,來!

class Solution {
    public int lengthOfLIS(int[] nums) {
         if (nums.length == 0) {
            return 0;
        }
        if (nums.length == 1) {
            return 1;
        }
        int[] dp = new int[nums.length];
        dp[0] = 1;
        int max = 1;
        for (int i = 1; i < nums.length; i++) {
             int count=0;
            //非必須連續,條件修改為截止到當前元素,比自己小的元素個數,注意不是所有前面小的相加,依靠
               for(int j=0;j<i;j++){
                  if(nums[i]>nums[j]){
                      count++;
                  }
               }
               dp[i]=count+1;
                max = Math.max(max, dp[i]);
        }
         return max;
        }
       
    
}

結果不對,debug發現“求當前元素小的元素個數”這個思路不對,這樣不是增加的了。必須是一直增加的。

第一版的思路結果是計算的連續的,第二版的思路不能保證是自增的。怎麼能保證自增並且包括前面所有的元素呢?

最初我們分解,dp陣列元素的含義是:截止當前元素的最長增加序列(注意不是必須連續的),所以在求當前元素i的dp值,其實就是i之前小於i的那些元素的dp值的最大值。修改後程式碼如下:

class Solution {
    public int lengthOfLIS(int[] nums) {
         if (nums.length == 0) {
            return 0;
        }
        if (nums.length == 1) {
            return 1;
        }
        int[] dp = new int[nums.length];
        dp[0] = 1;
        int max = 1;
        for (int i = 1; i < nums.length; i++) {
             int count=0;
            //非必須連續,條件修改為截止到當前元素,比自己小的元素個數,注意不是所有前面小的相加,依靠
               for(int j=0;j<i;j++){
                  if(nums[i]>nums[j]){
                      count=Math.max(count,dp[j]);
                  }
               }
               dp[i]=count+1;
                max = Math.max(max, dp[i]);
        }
         return max;
        }
       
    
}