您的位置:首页 > 其它

算法导论之最长递增子序列

2014-06-11 20:47 357 查看
习题15.4-5,15.4-6:长度为n的数组,寻找最长递增子序列。要求(1)时间复杂度O(n^2) (2)时间复杂度O(n lg n)

解:可以利用公共子序列来解,将数组a排序得到a',则a与a'的最大公共子序列即为所求,O(n * n)。下面的两种方法都是直接基于动态规划。

设输入序列为a[1 .. n],以a[i]作为最后一个元素的最长序列的长度为L[i],则L[i] = max{ L[j]+1 for j<i and a[j]<a[i] }。

计算出数组L之后如何反推出子序列呢?假设最大的L[i]是L[12]=5,那么子序列长为5,且最后一个元素是a[12]。从L[12]向前遍历,找到的第一个L[i]=4,那么a[i]就是倒数第二个元素。如下图所示。



时间复杂度为O(n*n)的算法见下面代码中的LongIncreaseSeq函数。

至于O(n lg n)的算法,对于所有长度为s的递增自序列,其中一定有一个是末尾元素最小的,将此最小末尾元素记录在MinEnd[s],那么根据书中提示易知,一定有MinEnd[1] < MinEnd[2] < ... < MinEnd[s] < MinEnd[s+1]。现在遍历a,对于每个a[i],用二分法找到a[i]在MinEnd中的插入位置s,即MinEnd[s-1] < a[i] <= MinEnd[s],那么可知将a[i]加在(s-1)的序列后面将得到长度为s的序列,又因为a[i] <= MinEnd[s],所以将MinEnd[s]更新为a[i]。在这个过程中还能顺便得到L数组。代码见函数LongIncreaseSeq_nlgn。

import bisect, random

#L[i] = max{ length of increasing sequences end at a[i] }
def findSeq(a, L):
seq = []
curPos = L.index(max(L))
seq.append(a[curPos])
for i in range(curPos-1, -1, -1):
if L[i] == L[curPos]-1:
curPos = i
seq.insert(0, a[curPos])
return seq

#O(n * n)
def LongIncreaseSeq(a):
L = [0]*len(a)
for i in range(len(a)):
longest = 1
for j in range(i):
if a[j] < a[i] and L[j]+1 > longest:
longest = L[j]+1
L[i] = longest
print L
print findSeq(a, L)
return max(L)

#O(n * lg n)
def LongIncreaseSeq_nlgn(a):
INF = max(a)+1
#MinEnd[i] = min { end element for all increasing sequences of length i+1 }
MinEnd = [INF]*len(a)
L = [0]*len(a)
for i in range(len(a)):
j = bisect.bisect_left(MinEnd, a[i])
MinEnd[j] = a[i]
L[i] = j+1;
print findSeq(a, L)
return max(L)

a = [random.randint(0, 20) for i in range(15)]
print a
print LongIncreaseSeq(a)
print LongIncreaseSeq_nlgn(a)


参考资料:

/article/8604735.html

/article/2939893.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: