1. 程式人生 > >leetcode刷題,總結,記錄,備忘300

leetcode刷題,總結,記錄,備忘300

leetcode300,Longest Increasing Subsequence

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

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.

Your algorithm should run in O(n2) complexity.

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

Credits:
Special thanks to @pbrother for adding this problem and creating all test cases.

Subscribe to see which companies asked this question

題目提示使用動態規劃的方法去做,雖然我之前也見過一些動態規劃的題,但是並沒有去好好了解這種演算法,於是自己也去讀了很多別人的部落格,並結合這個動態規劃的入門題目,進行了一些學習。動態規劃內容我也講不清楚,自己就是理解為一個大的問題,可以分解成一個一個的小問題,然後將每個小的問題的結果快取下來,然後合成最終問題的解,其中有些規則和使用條件的什麼的,實在太抽象了,不能很好的瞭解,先擱置,以具體題目為目標,再慢慢的瞭解。

言歸正傳,這個題目使用動態規劃來解,從第一個數開始,算出在每個下標上的最長遞增長度,然後取其中最大的一個即可。此時需要一個數組儲存每個下標位置上的結果,假設這個陣列為len,長度與目標陣列相同,並設其第一個元素為1。接著開始從目標陣列的第二個下標開始遍歷,巢狀的第二個迴圈從陣列頭開始,直到當前的下標,獲得當前下標為終點的序列的最大遞增長度,存放在len陣列中,一直遍歷完為止,將最大值返回。可能說起來比較抽象,看下程式碼,用實際數字試一下會更好理解。

這只是一種方法,巢狀迴圈,耗時比較多,還有一種二分法,先上第一種的程式碼。

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        if (nums.size() == 0)
        {
            return 0;
        }
        
        
        vector<int> len(nums.size());
        len[0] = 1;
        int maxlen = 1;
        
        for (int i = 1; i < nums.size(); ++i)
        {
            int max = 0;
            for (int j = 0; j < i; ++j)
            {
                if (nums[i] > nums[j] && len[j] > max)
                {
                    max = len[j];
                }
            }
            
            len[i] = max + 1;
            if (len[i] > maxlen)
            {
                maxlen = len[i];
            }
        }
        
        return maxlen;
    }
};
第二種方法為二分法,耗時比較少,這種方法非常巧妙。還是需要一個另外的陣列,用來儲存元素。首先將目標陣列第一個數放入臨時的陣列,然後從第二個下標開始遍歷目標陣列,與臨時陣列中最後一個數進行比較大小,如果大的話,將元素放入臨時陣列,並且用來計算長度的變數同時增加,如果小於的話,就在該臨時陣列中使用二分的方法尋找比他小的數,放在那個數前面,如果找不到的話,就直接放在頭部,,如果有已這個數開頭的又一個遞增序列的話,如果長度比之前的長,就會覆蓋之前的數,並使計算長度的變數變的更大,最後在遍歷完目標陣列之後,這個長度變數便是最終的結果,光說還是太抽象了,還是上程式碼。自己實際操作會更有助於理解。
class Solution {
public:
    
    int Bsearch(vector<int> len, int start, int end, int key)
    {
		if (start == end)
		{
			return key > len[start] ? start : start - 1;
		}

        while (start <= end)
        {
            int mid = (start + end) / 2;
            if (key > len[mid] && key < len[mid + 1])
            {
                return mid;
            }
            else if (key < len[mid])
            {
                end = mid - 1;
            }
            else if (key > len[mid])
            {
                start = mid + 1;
            }
            else if (key == len[mid])
            {
                return mid - 1;
            }
        }
        
        return 0;
    }
    
    int lengthOfLIS(vector<int>& nums) {
        if (nums.size() == 0)
        {
            return 0;
        }
        
        vector<int> len(nums.size() + 1);
        int maxlen = 1;
        int n;
        len[1] =  nums[0];
        
        for (int i = 1; i < nums.size(); ++i)
        {
            if (nums[i] > len[maxlen])
            {
                n = ++maxlen;
            }
            else
            {
                n = Bsearch(len, 1, maxlen, nums[i]) + 1;
            }
            
            len[n] = nums[i];
        }
        
        return maxlen;
    }
};
兩種演算法時長相差了90ms。。。。。