文章转载自:https://blog.youkuaiyun.com/wqtltm/article/details/81253935
版权归原作者。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.youkuaiyun.com/wqtltm/article/details/81253935
最长上升子序列有o(n^2)的复杂度这个就不说了,简单易懂,博客一大堆。我们今天来说说o(nlogn)复杂度的这个方法。
以前我学习这个算法的时候,看了很多博客,楞是看了很久才看懂,所以自己今天立志要把这个算法整的简单易懂一点儿
举个例子来讲解我的思路,例如我要找的序列是【 7 2 1 5 6 4 3 8 9 】
第一步 先拿出第一个数7
然后 长度为1的序列就是【7】,我表示为如下的形式
1:【7】
第二步加入2 ,我发现2比7小,那么我就用2代替7。这是因为如果后面出现了9 那么7,9和2,9都是一样的长的,看起来如果不换似乎也没什么问题。但是如果后面出现的不是9而是5,那么原本应该长度为2的序列2,5 就不会变成7,5(这是不合法的)。所以用2替换7是最合理的。结果就是
1:【2】
第三步加入1,继续替换2
1:【1】
第四步加入5,这个时候我们发现5比1大,所以我就可以扩展出长度为2的序列了,当然长度为1的序列依旧保留
1:【1】
2:【1,5】
为什么要保留长度为1的序列,是有道理的 如果这里出现的是9不是5 那么舍弃长度为1 的序列会有大麻烦。例如如果是1,9 后面要是再出现一个6,7那么最长序列应该是1,6,7如果长度为1的序列被舍弃了,那么1,9后面出现6,7就不能添加进去了。
第五步加入6
发现可以加入长度为1的序列扩展,长度2的序列,但是我们有了长度2的序列并且最后一位是5比6更优,所以长度为2的序列不变,但是6加入长度为2的序列可以扩展为长度3的序列
1:【1】
2:【1,5】
3:【1,5,6】
第六步加入4 ,同理4加入长度为1的序列不能改变它但是可以改变长度为2 的
1:【1】
2:【1,4】
3:【1,5,6】
第七加入3结果是
1:【1】
2:【1,3】
3:【1,5,6】
第八加入8结果就是长度为1的序列加入8扩展为【1,8】但是【1,3】比【1,8】优秀,所以舍弃,同理长度为2的加入2得到【1,3,8】也没有同样长度为3的序列【1,5,6】优秀,所以舍弃。但加入长度为3的序列可以得到长度4的序列【1,5,6,8】
1:【1】
2:【1,3】
3:【1,5,6】
4:【1,5,6,8】
因此我们发现了规律就是每加入一个数,找到一个长度为m的序列使得这个序列 的最后一位数比它小,长度为m+1的序列的最后一位数比它大,那么就用这位数更新长度为m+1的那个序列的最后一位数。但是如果长度m的序列是当前最长的那个序列,也就是 说意味着不存在m+1长度的序列,那么直接扩展出长度加1 的一个新序列。
最后我们加入9 发现它比最长长度的那个的最后一位还大,所以直接扩展新的序列【1,5,6,8,9】
1:【1】
2:【1,3】
3:【1,5,6】
4:【1,5,6,8】
5:【1,5,6,8,9】
#1.生成长度为N(arr的长度)的数组dp,dp[i]表示在以arr[i]结尾的情况下,arr[0…i]中的最长子序列。
#2.dp的初始值是1也是边界条件
#3.dp[i] = max(dp[j]+1,dp[i]) ,j in [0..i]
#o(n^2)
def subseq(data):
lenghth = len(data)
dp = [1 for i in range(lenghth)]
for i in range(lenghth):
for j in range(i):
if data[j]<data[i]:
dp[i] = max(dp[j]+1,dp[i])
return max(dp)
#1.这就是我的方法,复杂度o(nlogn)
def subseq_log(data):
def replace(dp,k):
left = 0
right = len(dp)-1
while left<right:
middle = (left+right)//2
if k==dp[middle]:
return
elif k<dp[middle]:
right=middle
else:
left = middle+1
dp[right] = k
lenghth = len(data)
dp = [data[0]]
for i in range(1,lenghth):
if data[i]>dp[-1]:
dp.append(data[i])
else:
replace(dp,data[i])
print(dp)
return len(dp)
if __name__=='__main__':
array = [7 ,2 ,1, 5, 6, 4, 3, 8, 9]
print(subseq(array))
print(subseq_log(array))
因此最长度长度是5。 一共遍历了n个数字,每个数字呢需要取寻找上面提到的那个长度为m的序列,可以用2分查找,因此复杂度是logn 因此整个算法复杂度是nlogn
其实仔细看看我上面的分析和别人的方法是一致的,如果把我的思路理解了再去看下面这篇文章,就可以把空间复杂度降低为o(n)而不是我现在的o(n^2)
其他的思路:https://blog.youkuaiyun.com/u013178472/article/details/54926531