您的位置:首页 > 其它

最长上升子序列

2017-02-11 21:38 148 查看
一个数的序列bi,当b1 < b2 < ... < bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1, a2, ..., aN),我们可以得到一些上升的子序列(ai1, ai2, ..., aiK),这里1 <= i1 < i2 < ... < iK <= N。比如,对于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等。这些子序列中最长的长度是4,比如子序列(1, 3, 5, 8).

思路:dp,即求N个序列的这个状态可以由K(1<=k<=n-1)个序列的状态推导得出

dp
:表示以第n个元素结尾的子序列的最长序列长度

if a
>a[k] dp
= max{1,1 + dp[k]} (1<=k<=n-1)

解法一:O(n2)

N2的时间复杂度关键在于每次求第N个元素的时候都遍历一遍前N-1个元素,然后取最大值。

解法二:O(nlgn)

nlgn的快速之处在于不是每次去遍历前N-1个元素,而是从一开始就维护一个栈,这个栈中保存的是当前序列的“伪最长子序列”。

举例:原序列为1,5,8,3,6,7

栈为1,5,8,此时读到3,用3替换5,得到1,3,8;

再读6,用6替换8,得到1,3,6;

再读7,得到最终栈为1,3,6,7。

最长递增子序列为长度4。

可以发现栈中的序列不一定是正确的,但是长度肯定是正确的。

因为维护栈的策略是(假设栈顶元素是top, 新元素是temp)

if temp > top, temp入栈

if temp <= top, 首先找出第一个大于等于temp的栈中元素,并用temp替代它。

public void LIS(int[] nums){
int[] stack = new int[nums.length];
int size = 0;
int[] dp = new int[nums.length];
for(int i = 0; i < nums.length; i ++){
if(i == 0){
dp[i] = 1;
stack[size] = nums[i];
size ++;
}
else{
if(nums[i] > stack[size - 1]){
stack[size] = nums[i];
size ++;
dp[i] = size;
}
else if(nums[i] == stack[size - 1]){
dp[i] = size;
}
else{
int low = 0;
int high = size - 1;
while(low <= high){
int mid = (low + high) / 2;
if(stack[mid] == nums[i]){
high = mid - 1;
}
else if(stack[mid] > nums[i]){
high = mid - 1;
}
else{
low = mid + 1;
}
}
stack[low] = nums[i];
dp[i] = low + 1;
}
}
}
for(int i = 0; i < size; i ++){
System.out.print(stack[i]+" ");
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: