LeetCode 300. Longest Increasing Subsequence
问题描述
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.
题解
作为一个刚玩动态规划的新手,这道新手题确实值得一做。
复杂度为O(n^2)的算法
我们可以将序列中的每个数字看做一个节点,当节点a
在另一个节点b
的前面且a<b
,我们就有a->b
这样一条边,示例中的序列我们可以画成如下的图:
可以从头遍历该序列,每个节点作为序列的最后节点时序列长度记为len(v)=max{len(ui)}
+1,其中ui
为边ui->v
的起点,因为它是一个有向无环图,故每个节点v
在计算之前,len(ui)
已经在它之前计算出来了。
看看伪代码:
for each node v in sequence:
max = 0
for each edge e(u, v):
if len(u) > max:
max = len(u)
len(v) = max
return max{len(vi)}
复杂度为O(nlogn)的算法
与前面算法不同的地方是我们使用了这样的一个数据结构(数组):
l[index]
为长度index+1
的所有递增子串最后元素最小的一个length(l)
代表当前找到的最长递增子串
看到使用这样一个数组,相信大家应该能明白我大概要做些什么了?仍然以示例数据举例子。首先数组l
长度为0,从头遍历这个序列,对遍历到的每个元素做如下操作:
- 利用二分法来判断元素在数组
l
中的位置,什么位置呢?举个例子,如果数组中已经有{1, 3, 5, 7}
,现在元素4
应该放在什么位置呢,嗯没错,放在5
这个地方并把5
换成4
对吧! - 上面说的是可以替换元素的情况,如果我们发现该元素比
l
中所有元素都大,怎么办了?我们就发现了一个更长的子序列呀,例如{1, 3, 5, 7}
,我们发现了9
,那么l
就变成了{1, 3, 5, 7, 9}
。
看起来很简单对吧,下面看看这个算法的代码:
class Solution {
private:
int biSearch(vector<int> &arr, int begin, int end, int e)
{
if (begin >= end)
return end;
int mid = (begin + end) / 2;
if (arr[mid] == e)
return mid;
if (arr[mid] > e)
return biSearch(arr, begin, mid, e);
else {
if (e < arr[mid + 1])
return mid + 1;
return biSearch(arr, mid + 1, end, e);
}
}
public:
int lengthOfLIS(vector<int> &nums)
{
if (nums.size() == 0)
return 0;
vector<int> l(nums.size());
int longest_index = 0;
l[longest_index] = nums[0];
for (size_t i = 1; i < nums.size(); i++)
{
int index = biSearch(l, 0, longest_index, nums[i]);
if (l[index] > nums[i])
l[index] = nums[i];
else if (l[index] == nums[i])
continue;
else
{
if (index == longest_index)
{
++longest_index;
l[longest_index] = nums[i];
}
}
}
return longest_index + 1;
}
};
复杂度分析
第一种算法,我们可以这么来估计复杂度,首先我们需要做的是遍历一遍整个序列,而序列中的每个元素我们需要找出max{len(ui)}
,而这一步的复杂度大概为n
,所以总复杂度为O(n^2)
。
第二种算法,与第一种相同的部分是我们都需要遍历一遍整个序列,但是得益于数组l
这样一个有序的数据结构,使得我们可以使用二分法来找到元素在l
中位置,这一步复杂度大概为logn
,所以总复杂度为O(nlogn)
。