一. 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?
Difficulty:Medium
TIME:30MIN
解法一
这道题是求序列的最长递增子序列(以下简称LIS),之前做过一道题是求序列的所有递增子序列Increasing Subsequences,时间复杂度为
2n
。因此,当然不能用暴力搜索的办法来求LIS。
当然,也很容易能够看出来,这道题满足最优子结构,因此可以用动态规划来求解,LIS的最优子结构为:
令 X=<x1,x2,...,xi−1,xi> 带求解序列的的任意一个前缀,那么该前缀的LIS可以表示为 dp[i] :
- dp[i]=max(dp[1],dp[2],...,dp[i−1])+1
代码如下:
int lengthOfLIS(vector<int>& nums) {
if(nums.size() <= 1)
return nums.size();
vector<int> dp(nums.size(),0);
int result = 0;
for(int i = 0; i < nums.size(); i++) {
for(int j = 0; j < i; j++) {
if(nums[i] > nums[j] && dp[i] < dp[j]) {
dp[i] = dp[j];
}
}
dp[i]++;
if(dp[i] > result)
result = dp[i];
}
return result;
}
代码的时间复杂度为 O(n2) 。
解法二
当然,这道题还有一个巧妙的解法,可以进一步降低代码的时间复杂度,不过要用到LIS的某些特性。
我们可以采用一个数组来直接保存LIS,比如对于序列
<9,2,5,3,4>
<script type="math/tex" id="MathJax-Element-1159"><9,2,5,3,4></script>来说,当遍历到9的时候,LIS为
<9>
<script type="math/tex" id="MathJax-Element-1160"><9></script>,当遍历到2的时候,我们发现2比9要小,不管后面出现什么数,以2开始序列肯定比9开始的序列要长,最差的情况就是相等,因此,我们可以直接用2替换掉9,这个时候LIS为
<2>
<script type="math/tex" id="MathJax-Element-1161"><2></script>,按照这个思路,LIS的变化将会如下:
- <9>−><2>−><2,5>−><2,3>−><2,3,4> <script type="math/tex" id="MathJax-Element-1162"><9> -> <2> -> <2,5> -><2,3> -> <2,3,4></script>
这样的替换过程一定会得到一个最长递增子序列。
int lengthOfLIS(vector<int>& nums) {
vector<int> v;
for(int i = 0; i < nums.size(); i++) {
auto it = lower_bound(v.begin(), v.end(), nums[i]);
if(it == v.end())
v.push_back(nums[i]);
else
*it = nums[i];
}
return v.size();
}
代码的时间复杂度为 O(nlogn) 。
知识点
C++中的Binary search
C++的算法库中有很多很实用的算法,这里就用到了和二分查找有关的算法,不算find的话有4种,分别是lower_bound,upper_bound,equal_range和binary_search,介绍如下:
- lower_bound:Returns an iterator pointing to the first element in the range [first,last) which does not compare less than val(>=).
- upper_bound:Returns an iterator pointing to the first element in the range [first,last) which compares greater than val(>).
- equal_range:Returns the bounds of the subrange that includes all the elements of the range [first,last) with values equivalent to val.
- binary_search:Returns true if any element in the range [first,last) is equivalent to val, and false otherwise.
具体用法可以参照http://www.cplusplus.com/reference/algorithm/的介绍。
总结
这道题用到了动态规划的思想,第二种解法在最优子结构的构造方面比第一种解法更加巧妙,另外可以比较一下求LPSLongest Palindromic Subsequence和求LIS有什么不一样的地方。