1. 程式人生 > >LeetCode 300. Longest Increasing Subsequence —— 最長上升子序列(Java)

LeetCode 300. Longest Increasing Subsequence —— 最長上升子序列(Java)

什麽 || 序列 無法 tput while 多少 需要 con

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?


解法一,O(n2):

直觀想一下,選中一個元素,以這個元素為結尾的子序列前面有多少個比他值小的元素,那麽以它為結尾的上升子序列就是多少再加一。即當前狀態可以由之前的一個或一些狀態推導出來,所以可以用動態規劃。建立一個一維數組dp,dp[i]表示以nums[i]結尾的最長上升子序列長度。初始都置為1。對於原數組每個元素,二重循環從頭遍歷原數組,每當找到一個比當前元素小的值,證明至少可以形成一個dp[j]+1的上升子序列,所以dp[i] = max(dp[i], dp[j] + 1),而dp[j]之前已經求得。

解法二,O(n log n):

還是想一下“人工”做這個題是什麽過程。按照上面的例子來分析:

首先看到10,加入備選集,備選集合為{10};

之後看到了9,沒有形成上升序列,那麽9不應該加入備選集合。但是因為9小於10,所以如果把10替換成9會增加接下來產生上升序列的機會,且並不影響備選集合元素的個數(因為是替換),所以替換掉,備選集現在有{9};

遇到2道理同上,替換掉9,備選集變成{2};

遇到5,這時候形成了上升序列,此時應該是添加到備選集合,變為{2,5};

遇到3,沒有形成上升序列,但還是道理同加入9的情況,如果此時把5替換成3,會增加接下來形成上升序列的機會,且備選集保持上升,並且個數也沒變,所以替換掉5,備選集變成{2,3};

遇到7,同遇到5,添加元素,備選集{2,3,7};

遇到101,同上,備選集{2,3,7,101};

遇到18,還是一樣,雖然沒有形成上升序列,但是如果把101替換掉,那麽接下來形成上升序列的機會會增加,並且備選集的上升屬性和元素個數都不變,所以替換,備選集變為{2,3,7,18}。

至此所有元素添加完畢,備選集的元素個數就是最長上升子序列長度。但這裏註意,備選集裏面的元素並不是最後最長子序列的元素。因為在尋找最長子序列的過程中,目標是盡可能的讓以後形成上升序列的機會增加,所以進行了替換。

“人工”做出來之後,只要用程序實現思考過程就好。總結起來就是:

如果遇到的元素比備選集合裏面的元素都大,那麽就添加進去,使得上升序列長度增加;

如果遇到的元素比備選集合裏最後一個元素小,那麽代表它無法被添加到備選集。但是為了使後面得到上升序列的機會增加,需要在不破壞集合上升屬性和元素總數的情況下,替換掉備選集中的元素,那麽就是替換掉大於他的元素中最小的那個,這樣才能滿足條件。

這時候,發現備選集一直是保持有序,尋找替換元素的時候就可以用到二分查找,得到O(n log n)的時間復雜度。其中還要註意的是如果元素已經在備選集合中,是不需要任何操作的,因為它並不能增加上升序列的長度,也不會增加之後遇到上升序列的機會,所以直接跳過。


Java(解法一)

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

Java(解法二)

class Solution {
    public int lengthOfLIS(int[] nums) {
        if (nums == null || nums.length == 0) return 0;
        List<Integer> dp = new ArrayList<>();
        dp.add(nums[0]);
        for (int i = 1; i < nums.length; i++) {
            if (dp.contains(nums[i])) continue;
            else if (nums[i] > dp.get(dp.size()-1)) dp.add(nums[i]);
            else if (nums[i] < dp.get(dp.size()-1)) {
                int l = 0, r = dp.size()-1;
                while (l < r) {
                    int mid = l + (r - l) / 2;
                    if (dp.get(mid) < nums[i]) l = mid + 1;
                    else r = mid;
                }
                dp.set(r, nums[i]);
            }
        }
        return dp.size();
    }
}

LeetCode 300. Longest Increasing Subsequence —— 最長上升子序列(Java)