LeetCode 300. Longest Increasing Subsequence

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

Example:c++

Input: Output: 4 
Explanation: The longest increasing subsequence is , therefore the length is . [10,9,2,5,3,7,101,18][2,3,7,101]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?spa

 

很是經典的一道動態規劃題目,經常使用解法有兩個.net

一、暴力搜索c++11

時間複雜度O(N^2),空間複雜度O(N),f[i]表示以num[i]結尾的上升子序列的長度,外層循環遍歷整個序列,複雜度O(n),內層循環則從第一個數遍歷到外層循環的數字(不含)爲止,轉移方程爲f[i] = max(f[i],f[j]+1)代碼以下:code

 1 class Solution {
 2 public:
 3     int lengthOfLIS(vector<int>& nums) {
 4         if (nums.size() == 0)
 5             return 0;
 6         vector<int> v;
 7         int len = 1;
 8         for (int i = 0; i < nums.size(); i++)
 9         {
10             v.push_back(1);
11             for (int j = 0; j < i; j++)    //這裏的能夠是小於,也能夠是小於等於
12                 if (nums[j] < nums[i])    //這裏根據題目狀況,若是要求嚴格單調上升,就必須是小於,不然應該是小於等於
13                     v[i] = max(v[i], v[j]+1);
14             if (v[i] > len)
15                 len = v[i];
16                     
17         }
18         return len;
19     }
20 };

 

二、二分搜索htm

時間複雜度O(N*logN),空間複雜度O(N),咱們能夠用一個數組,來記錄當前已經造成的遞增子序列,該數組必定是單調不減的,咱們所但願的是末尾的數字儘可能小,這樣纔會使得更多的數字有機會加入進來。外層循環須要遍歷整個序列,內層循環則利用有序性進行二分查找,找到該數字在咱們定義的數組中的位置,而後更新,最終返回該數組的長度便可,可是這麼作要注意咱們只保證長度是正確的,不保證該數組是一個最長上升子序列。blog

代碼以下:ip

 1 class Solution {
 2 public:
 3     int lengthOfLIS(vector<int>& nums) {
 4         if (nums.size() == 0)
 5             return 0;
 6         vector<int> res;
 7         res.push_back(nums[0]);
 8         for (int i = 0; i < nums.size(); i++)
 9         {
10             int left = 0, right = res.size()-1;
11             if (nums[i] > res.back())
12                 res.push_back(nums[i]);
13             else
14             {
15                 while (left < right)
16                 {
17                     int mid = (left + right) / 2;
18                     if (res[mid] < nums[i])
19                         left = mid + 1;
20                     else
21                         right = mid;
22                 }
23                 res[right] = nums[i];
24             }
25         }
26         return res.size();
27     }
28 };

 易錯點:6,7行,能夠利用c++11新特性寫成 res{nums[0]},可是千萬別錯寫成res(nums[0])

另外,這道題的細節方面也值得注意,17行能夠用移位運算代替除法提高速度,19行和21行是變形的二分查找,由於右邊界搜索的速度放慢了,right = mid 而不是right = mid-1,這麼作個人理解是「二分查找容許找不到返回-1,而咱們這裏必定會找到一個適合插入的位置,因此右邊界搜索速度減慢」,固然你也許會問若是把左邊界搜索速度放慢右邊界不變能夠嗎?好比19行改成left = mid, 21行改成 rigth = mid - 1, 答案是不行,會陷入死循環,[10,9,2,5,3,7,101,18]就過不了。由於咱們須要構造的是一個右緊密序列,也就是容許mid落在right上。
還有,15行必須爲小於號,而不是小於等於,由於那樣會陷入死循環,細節不少,須要注意!

 

參考連接:

http://www.javashuo.com/article/p-tjbllqud-eg.html

http://www.cnblogs.com/grandyang/p/4938187.html

http://www.javashuo.com/article/p-kavmicpr-dy.html

http://www.javashuo.com/article/p-hqpbmfjn-ha.html

相關文章
相關標籤/搜索