在无序列表中找到最长的有序序列

时间:2019-05-03 22:06:49

标签: python algorithm

我试图在无序列表中找到最长的序列并返回其索引。有改善的空间吗?我如何找出最坏的情况呢?我对算法和运行时间很陌生。谢谢!

def find_longest_array(array):
    start = 0
    end = 0
    for left in range(len(array)):
        for right in range(left + 1, len(array)):
            sub_array = array[left:right+1]
            if sorted(sub_array) == sub_array:
                if len(sub_array) > end - start:
                    start, end = left, right
                if end == len(array):
                    return start, end
            else:
                break
    return start, end

4 个答案:

答案 0 :(得分:3)

您的程序运行时间为O(n ^ 3 log n),效率很低。

我们如何得出O(n ^ 3 log n)数字?

  1. 您一次考虑每个子数组:有n ^ 2个子数组。
  2. 然后,您对每个数组进行排序并将其与自身进行比较:对每个数组进行nlogn。

有一个线性时间方法。

dp[i]表示在位置i处结束的最长顺序。

然后:

dp[i]可以由

组成
  • 要么继续以i - 1结尾的前一个序列(只有在arr[i] >= arr[i - 1]才有可能保持顺序)

  • ,或在此时开始新的有序序列。

因此,为所有dp[i] = (dp[i - 1] + 1) if arr[i] >= arr[i - 1] or 1≠0计算i

最后,返回所有dp[i]中的最大值。

如果要返回最大数组的实际左边界和右边界,请从i向后进行迭代,其中i是最大的dp[i]


实际上,您可以完全消除dp,而只需跟踪当前序列的长度以及最大序列的长度和位置,这样远

但是我觉得这种方法更容易理解,更容易正确编写。

答案 1 :(得分:2)

我仍然想回答这个问题,而且我没有足够的代表发表评论。可以吗?

是的,还有改进的空间,您在内部循环上的迭代时间超出了必要,并反复对整个内部列表进行排序。

改进: 首先,您一次只需要比较两个值(上下两个)。其次,一旦发现不符合要求的内容,就可以摆脱嵌套循环。 最后一点只是为了调用更少的函数:我个人喜欢使用python的enumerate而不是range(len):

list = [1, 5, 78, 2, 3, 6, 8, 1, 7, 2, 9, 3, 7, 2]

print(list)
length = 0 
for index, item in enumerate(list):
    a = item
    for index2,following in enumerate(list[index:]):
        b = following
        if sorted([a,b]) != [a,b]: #comparison here, looking for break in pattern
            if index2>length:
                length = index2
                start = index
            break
        a = b #update current value to continue

print ("Sequence {} long, between {} and {}".format(length, start, start+length-1))
print(list[start:start+length])

输出:

[1, 5, 78, 2, 3, 6, 8, 1, 7, 2, 9, 3, 7, 2]
Sequence 4 long, between 3 and 6
[2, 3, 6, 8]

但是我确定里面有一个while循环解决方案;)

答案 2 :(得分:2)

在线性时间(时间复杂度为( O(n)))中解决此问题的更有效方法是跟踪当前有序序列的起始索引以及起始和结束时间最长序列的索引,如果当前有序序列的长度大于先前的最长​​序列,则更新最长序列的开始和结束索引。如果遇到乱序项,将起始索引重置为当前索引:

def find_longest_array(array):
    longest_start = longest_end = start = 0
    for i, n in enumerate(array):
        if i:
            if array[i - 1] > n:
                start = i
            elif i - start > longest_end - longest_start:
                longest_start, longest_end = start, i
    return longest_start, longest_end

这样:

find_longest_array((2, 1, 2, 3, 2, 1, 5, 2, 4, 5))

返回:

(1, 3)

答案 3 :(得分:1)

您可以在O(n)时间内完成操作,而无需使用zip和list理解来进行for循环:

L = [1, 5, 78, 2, 3, 6, 8, 1, 7, 2, 9, 3, 7, 2]

breaks = [0] + [ i+1 for (i,n),p in zip(enumerate(L),L[1:]) if n>p ] + [len(L)]
size,start,end = max([ (e-s,s,e) for s,e in zip(breaks,breaks[1:]) ])
  • breaks标识递增序列开始的索引

    [0,3,7,9,9,11,13,14]

  • zip(breaks,breaks[1:])然后将它们配对成连续的范围

    [(0,3),(3,7),(7,9),(9,11),(11,13),(13,14)]

  • 获得最大值(按长度)可直接得出答案。

    print(size, start, end, L[start:end]) # 4 3 7 [2, 3, 6, 8]