[LeetCode] 300. Longest Increasing Subsequence 最長遞增子序列

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

For example,
Given [10, 9, 2, 5, 3, 7, 101, 18],
The longest increasing subsequence is [2, 3, 7, 101], therefore the length is 4. Note that there may be more than one LIS combination, it is only necessary for you to return the length.java

Your algorithm should run in O(n2) complexity.python

Follow up: Could you improve it to O(n log n) time complexity?算法

 

解法1: 動態規劃DP,相似brute force的解法,維護一個一維dp數組,其中dp[i]表示以nums[i]爲結尾的最長遞增子串的長度。Time: O(n^2)數組

設長度爲N的數組爲{a0,a1, a2, ...an-1),則假定以aj結尾的數組序列的最長遞增子序列長度爲L(j),則L(j)={ max(L(i))+1, i<j且a[i]<a[j] }。也就是說,須要遍歷在j以前的全部位置i(從0到j-1),找出知足條件a[i]<a[j]的L(i),求出max(L(i))+1即爲L(j)的值。最後,咱們遍歷全部的L(j)(從0到N-1),找出最大值即爲最大遞增子序列。時間複雜度爲O(N^2)。app

例如給定的數組爲{5,6,7,1,2,8},則L(0)=1, L(1)=2, L(2)=3, L(3)=1, L(4)=2, L(5)=4。因此該數組最長遞增子序列長度爲4,序列爲{5,6,7,8}。ide

解法2: 二分查找法Binary Search, 維護一個單調遞增子序列,若是當前值小於單調遞增子序列中的某個元素,則替換之,由於單調遞增子序列可否增加,值取決於最後一個元素,替換內部的元素並不影響。Time: O(nlogn)優化

假設存在一個序列d[1..9] = { 2,1 ,5 ,3 ,6,4, 8 ,9, 7},能夠看出來它的LIS長度爲5。
定義一個序列B,而後讓 i = 1 to 9 逐個考察這個序列。用一個變量Len來記錄如今最長LIS長度。
首先,把d[1]有序地放到B裏,令B[1] = 2,就是說當只有1一個數字2的時候,長度爲1的LIS的最小末尾是2。這時Len=1
而後,把d[2]有序地放到B裏,令B[1] = 1,就是說長度爲1的LIS的最小末尾是1,d[1]=2已經沒用了,很容易理解吧。這時Len=1
接着,d[3] = 5,d[3]>B[1],因此令B[1+1]=B[2]=d[3]=5,就是說長度爲2的LIS的最小末尾是5,很容易理解吧。這時候B[1..2] = 1, 5,Len=2
再來,d[4] = 3,它正好加在1,5之間,放在1的位置顯然不合適,由於1小於3,長度爲1的LIS最小末尾應該是1,這樣很容易推知,長度爲2的LIS最小末尾是3,因而能夠把5淘汰掉,這時候B[1..2] = 1, 3,Len = 2
繼續,d[5] = 6,它在3後面,由於B[2] = 3, 而6在3後面,因而很容易能夠推知B[3] = 6, 這時B[1..3] = 1, 3, 6,仍是很容易理解吧? Len = 3 了噢。
第6個, d[6] = 4,你看它在3和6之間,因而咱們就能夠把6替換掉,獲得B[3] = 4。B[1..3] = 1, 3, 4, Len繼續等於3
第7個, d[7] = 8,它很大,比4大,嗯。因而B[4] = 8。Len變成4了
第8個, d[8] = 9,獲得B[5] = 9,嗯。Len繼續增大,到5了。
最後一個, d[9] = 7,它在B[3] = 4和B[4] = 8之間,因此咱們知道,最新的B[4] =7,B[1..5] = 1, 3, 4, 7, 9,Len = 5。
因而咱們知道了LIS的長度爲5。
注意,這個1,3,4,7,9不是LIS,它只是存儲的對應長度LIS的最小末尾。有了這個末尾,咱們就能夠一個一個地插入數據。雖然最後一個d[9] = 7更新進去對於這組數據沒有什麼意義,可是若是後面再出現兩個數字 8 和 9,那麼就能夠把8更新到d[5], 9更新到d[6],得出LIS的長度爲6。
而後發現一件事情:在B中插入數據是有序的,並且是進行替換而不須要挪動——也就是說,可使用二分查找,將每個數字的插入時間優化到O(logN)~~~~~因而算法的時間複雜度就下降到了O(NlogN)~!spa

Java: DPcode

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

Java:  BS

public class Solution {  
    public int lengthOfLIS(int[] nums) {  
        int[] increasing = new int[nums.length];  
        int size = 0;  
        for(int i=0; i<nums.length; i++) {  
            int left=0, right=size-1;  
            while (left<=right) {  
                int m=(left+right)/2;  
                if (nums[i] > increasing[m]) left = m + 1;  
                else right = m - 1;  
            }  
            increasing[left] = nums[i];  
            if (left==size) size ++;  
        }  
        return size;  
    }  
}

Java: TreeSet

public class Solution {  
    public int lengthOfLIS(int[] nums) {  
        if (nums == null || nums.length == 0) return 0;  
        int max = 1;  
        TreeSet<Integer> ts = new TreeSet<>(new Comparator<Integer>() {  
            @Override  
            public int compare(Integer i1, Integer i2) {  
                return Integer.compare(nums[i1], nums[i2]);  
            }  
        });  
        int[] lens = new int[nums.length];  
        Arrays.fill(lens, 1);  
        for(int i=0; i<nums.length; i++) {  
            if (ts.contains(i)) ts.remove(i);  
            ts.add(i);  
            Set<Integer> heads = ts.headSet(i);  
            for(int head: heads) {  
                lens[i] = Math.max(lens[i], lens[head] + 1);  
            }  
            max = Math.max(max, lens[i]);  
        }  
        return max;  
    }  
}

Python: BS, T: O(nlogn), S: O(n)

class Solution(object):
    def lengthOfLIS(self, nums):
        LIS = []
        def insert(target):
            left, right = 0, len(LIS) - 1
            # Find the first index "left" which satisfies LIS[left] >= target
            while left <= right:
                mid = left + (right - left) / 2
                if LIS[mid] >= target:
                    right = mid - 1
                else:
                    left = mid + 1
            # If not found, append the target.
            if left == len(LIS):
                LIS.append(target);
            else:
                LIS[left] = target

        for num in nums:
            insert(num)

        return len(LIS)

Python: DP, T: O(n^2), S: O(n)

class Solution(object):
    def lengthOfLIS(self, nums):
        dp = []  # dp[i]: the length of LIS ends with nums[i]
        for i in xrange(len(nums)):
            dp.append(1)
            for j in xrange(i):
                if nums[j] < nums[i]:
                    dp[i] = max(dp[i], dp[j] + 1)

        return max(dp) if dp else 0

Python: wo

class Solution(object):
    def lengthOfLIS(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if not nums:
            return 0
        n = len(nums)
        dp = [1] * n
        max_len = 1
        for i in xrange(n):
            for j in xrange(i):
                if nums[i] > nums[j]:
                    dp[i] = max(dp[i], dp[j] + 1)
                    max_len = max(max_len, dp[i])
                              
        return max_len   

C++:DP

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
    if (nums.size() == 0) return 0;
        vector<int> dp(nums.size(), 1);
        int res = 1;
        for (int i = 1; i < nums.size(); ++i){
            for (int j = 0; j < i; ++j){
                if (nums[j] < nums[i]){
                    dp[i] = max(dp[i], 1+dp[j]);
                }
            }
            res = max(res, dp[i]);
        }
        return res;
    }
};

C++:BS

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        if (nums.empty()) return 0;
        vector<int> ends{nums[0]};
        for (auto a : nums) {
            if (a < ends[0]) ends[0] = a;
            else if (a > ends.back()) ends.push_back(a);
            else {
                int left = 0, right = ends.size();
                while (left < right) {
                    int mid = left + (right - left) / 2;
                    if (ends[mid] < a) left = mid + 1;
                    else right = mid;
                }
                ends[right] = a;
            }
        }
        return ends.size();
    }
}; 

C++:BS

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        vector<int> dp;
        for (int i = 0; i < nums.size(); ++i){

            int lo = 0, hi = dp.size();
            while (lo < hi){
                int mi = (lo + hi)/2;
                if (dp[mi] < nums[i]) lo = mi + 1;
                else hi = mi;
            }
            if (hi == dp.size())
                dp.push_back(nums[i]);
            else dp[hi] = nums[i];
        }
        return dp.size();
    }
};

   

相似題目:

[LeetCode] 354. Russian Doll Envelopes 俄羅斯套娃信封 

[LeetCode] 673. Number of Longest Increasing Subsequence 最長遞增序列的個數

[LeetCode] 674. Longest Continuous Increasing Subsequence 最長連續遞增序列

 

All LeetCode Questions List 題目彙總

相關文章
相關標籤/搜索