2011-12-29 77 views
11

举例来说,如果我有一个列表在python中,如何有效地找到不一定相邻的列表中最大的连续数字集?

[1,4,2,3,5,4,5,6,7,8,1,3,4,5,9,10,11] 

该算法应该返回[1,2,3,4,5,6,7,8,9,10,11。

为了澄清,最长的列表应该前进。我想知道什么是算法有效的方法来做到这一点(最好不是O(n^2))?

此外,我打开一个解决方案,而不是在Python中,因为算法是重要的。

谢谢。

+2

为什么不'[1,2,3,4,5,6,7,8 ,9,10,11]'。我认为没有理由不包括这些数字,因为他们不必相邻。 – Serdalis 2011-12-29 06:37:29

+0

对不起,我的错。感谢您的更正。 – dangerChihuahua007 2011-12-29 06:39:50

+2

最长的连续序列可以从1以外的数字开始吗? – 2011-12-29 06:40:13

回答

13

下面是一个简单的通O(n)的解决方案:

s = [1,4,2,3,5,4,5,6,7,8,1,3,4,5,9,10,11,42] 
maxrun = -1 
rl = {} 
for x in s: 
    run = rl[x] = rl.get(x-1, 0) + 1 
    print x-run+1, 'to', x 
    if run > maxrun: 
     maxend, maxrun = x, run 
print range(maxend-maxrun+1, maxend+1) 

逻辑可能更多一点不言自明,如果你认为在该终端和运行的范围,而不是单个变量的条件长度:

rl = {} 
best_range = xrange(0) 
for x in s: 
    run = rl[x] = rl.get(x-1, 0) + 1 
    r = xrange(x-run+1, x+1) 
    if len(r) > len(best_range): 
     best_range = r 
print list(best_range) 
+2

+1起首! – jimifiki 2011-12-29 10:29:16

+0

@RaymondHettinger - 最后一行应该是:'print range(maxend-maxrun + 1,maxend + 1)'?否则,对于s = [1,4,2,3,5,4,9,10,11,5,6,7,8,1,3,4,5]'我只得到'[4,5, 6,7,8]',而不是[[1,2,3,4,5,6,7,8]]。 – PaulMcG 2011-12-29 11:45:04

+0

@nightcracker - 你运行它,并得到一个IndexError,或者你只是在你的脑海中运行这个?链接赋值从右到左工作,并且rl.get的默认值为0,所以没有IndexError。并且由于rl [1]得到0 + 1 = 1的值,所以它可以被复制到'run',并且再次没有IndexError。尝试运行这个,它确实工作正常。 – PaulMcG 2011-12-29 12:34:05

-2

这应该做的伎俩(为O(n)):

target = 1 
result = [] 
for x in list: 
    for y in result: 
     if y[0] == target: 
      y[0] += 1 
      result.append(x) 

对于任何起始号码,这个工程:

result = [] 
for x in mylist: 
    matched = False 
    for y in result: 
     if y[0] == x: 
      matched = True 
      y[0] += 1 
      y.append(x) 
    if not matched: 
     result.append([x+1, x]) 
return max(result, key=len)[1:] 
+0

+1,除非序列可以从除1以外的数字开始。 – 2011-12-29 06:41:29

+5

这将从1开始找到* first *,而不是最大的。'[2,3,4,5,1,2]' – 2011-12-29 06:41:41

+0

哇,这很聪明,谢谢。但是[1,2,3,4,12,13,14]是怎么样的呢?这个算法是否会返回'[1,2,3]'? – dangerChihuahua007 2011-12-29 06:43:22

2

您可以使用Patience Sort实施Largest Ascending Sub-sequence Algorithm

def LargAscSub(seq): 
    deck = [] 
    for x in seq: 
     newDeck = [x] 
     i = bisect.bisect_left(deck, newDeck) 
     deck[i].insert(0, x) if i != len(deck) else deck.append(newDeck) 
    return [p[0] for p in deck] 

这里是测试结果

>>> LargAscSub([1,4,2,3,5,4,5,6,7,8,1,3,4,5,9,10,11]) 
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] 
>>> LargAscSub([1, 2, 3, 11, 12, 13, 14]) 
[1, 2, 3, 11, 12, 13, 14] 
>>> LargAscSub([11,12,13,14]) 
[11, 12, 13, 14] 

复杂的顺序是Ø(nlogn)

有在他们声称可以实现Ø(n.loglogn)维基链接一个音符依靠Van Emde Boas tree

+2

结果是否必须是*连续*整数? – srgerg 2011-12-29 06:53:46

+0

@srgerg,只要检查Serdalis上面的评论问题和Chi Zeng的回复 – Abhijit 2011-12-29 06:58:45

+0

它不是最大的上升,它是最大的连续上升。 – jknupp 2011-12-29 07:27:13

3

不是那么聪明,不是O(n),可以使用一点优化。但它的工作。

def longest(seq): 
    result = [] 
    for v in seq: 
    for l in result: 
     if v == l[-1] + 1: 
     l.append(v) 
    else: 
     result.append([v]) 
    return max(result, key=len) 
+0

Arrrrg ... 10秒前我打“发布你的答案”......你赢了! ;)+1 – mac 2011-12-29 06:56:24

+0

其实没有* O *(n)实现这个:-) – Abhijit 2011-12-29 07:06:30

+0

这是O(n^2),和我的一样。需要考虑一种不同的方法。 – jknupp 2011-12-29 08:15:29

1

如何使用修改Radix Sort?正如JanneKarila指出的解决方案不是O(n)。它使用基数排序,其中维基百科说Radix sort's efficiency is O(k·n) for n keys which have k or fewer digits.

这只会在你知道我们处理的数字范围,所以这将是第一步。

  1. 在首发名单找到最低,l和最高的,h数量看每个元素。在这种情况下l为1和h是11。注意,如果你已经知道的范围内由于某些原因,你可以跳过这一步。

  2. 创建一个结果列表我们的范围的大小,并将每个元素设置为null。

  3. 只看该列表中的每个元素,并根据需要在适当的地方将它们添加到结果列表。即,该元件是一个如图4所示,在位置4 result[element] = starting_list[element]添加4到结果列表中。如果你愿意,你可以丢弃重复的内容,它们只会被覆盖。

  4. 通过结果列表中去寻找最长的序列,没有任何空值。保持一个element_counter知道我们要找什么元素在结果列表中。保持curr_start_element设置为当前序列的开始元素,并保持当前序列的长度为curr_len。同时保留一个longest_start_element和一个`longest_len',它将从零开始,并在我们移动列表时进行更新。

  5. 返回结果列表开始longest_start_element并采取longest_len

编辑:代码添加。测试和工作

#note this doesn't work with negative numbers 
#it's certainly possible to write this to work with negatives 
# but the code is a bit hairier 
import sys 
def findLongestSequence(lst): 
    #step 1 
    high = -sys.maxint - 1 

    for num in lst: 
     if num > high: 
      high = num 

    #step 2 
    result = [None]*(high+1) 

    #step 3 
    for num in lst: 
     result[num] = num 

    #step 4 
    curr_start_element = 0 
    curr_len = 0 
    longest_start_element = -1 
    longest_len = -1 

    for element_counter in range(len(result)): 
     if result[element_counter] == None: 

      if curr_len > longest_len: 
       longest_start_element = curr_start_element 
       longest_len = curr_len 

      curr_len = 0 
      curr_start_element = -1 

     elif curr_start_element == -1: 
      curr_start_element = element_counter 

     curr_len += 1 

    #just in case the last element makes the longest 
    if curr_len > longest_len: 
     longest_start_element = curr_start_element 
     longest_len = curr_len 


    #step 5 
    return result[longest_start_element:longest_start_element + longest_len-1] 
+0

第4步迭代结果列表n次,所以这不是O(n)。 – jknupp 2011-12-29 07:54:03

+0

@jknupp不,你只需要经历一次。这与从列表中查找最大值相同。除了它在列表中找到最长的序列。假设list ='[1,2,3,null,5,6,7,8,null,10]'我看到'[1,2,3]'长度为3,所以我保存了开始索引。然后看看'[5,6,7,8]'是长度4,所以更新最长的索引/长度变量。 ''[8]'不会改变它。一个循环,发现最长。 – 2011-12-29 08:12:12

+0

O(n)中的n是指输入列表的大小。值的范围可以大得多,并且与列表的长度无关。 – 2011-12-29 08:50:33

0

如果结果确实必须的连续上升的整数,而不是仅仅的递增整数子序列,那么就没有必要记住每个完整连续的子序列,直到你确定哪个最长,你只需要记住每个子序列的起始值和结束值。所以,你可以做这样的事情:

def longestConsecutiveSequence(sequence): 
    # map starting values to largest ending value so far 
    map = collections.OrderedDict() 

    for i in sequence: 
     found = False 
     for k, v in map.iteritems(): 
      if i == v: 
       map[k] += 1 
       found = True 

     if not found and i not in map: 
      map[i] = i + 1 

    return xrange(*max(map.iteritems(), key=lambda i: i[1] - i[0])) 

如果我在原来的样本数据运行这个(即[1,4,2,3,5,4,5,6,7,8,1,3,4,5,9,10,11])我得到:

>>> print list(longestConsecutiveSequence([1,4,2,3,5,4,5,6,7,8,1,3,4,5,9,10,11])) 
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] 

如果我对作者Abhijit的样品[1,2,3,11,12,13,14]的一个运行它,我得到:

>>> print list(longestConsecutiveSequence([1,2,3,11,12,13,14])) 
[11, 12, 13, 14] 

遗憾的是,这个算法在最坏的情况下是O(n * n)。

0

警告:这是cheaty办法做到这一点(又名我使用Python ...)

import operator as op 
import itertools as it 

def longestSequence(data): 

    longest = [] 

    for k, g in it.groupby(enumerate(set(data)), lambda(i, y):i-y): 
     thisGroup = map(op.itemgetter(1), g) 

     if len(thisGroup) > len(longest): 
      longest = thisGroup 

    return longest 


longestSequence([1,4,2,3,5,4,5,6,7,8,1,3,4,5,9,10,11, 15,15,16,17,25]) 
0

您需要最大连续总和Optimal Substructure):

def msum2(a): 
    bounds, s, t, j = (0,0), -float('infinity'), 0, 0 

    for i in range(len(a)): 
     t = t + a[i] 
     if t > s: bounds, s = (j, i+1), t 
     if t < 0: t, j = 0, i+1 
    return (s, bounds) 

这是动态编程的一个示例,并且是O(N)

0

O(n)的解决方案工作,即使序列不启动从第一个元素。如果len(A)= 0。

A = [1,4,2,3,5,4,5,6,7,8,1,3,4,5,9,10,11] 
def pre_process(A): 
    Last = {} 
    Arrow = [] 
    Length = [] 
    ArgMax = 0 
    Max = 0 
    for i in xrange(len(A)): 
     Arrow.append(i) 
     Length.append(0) 
     if A[i] - 1 in Last: 
      Aux = Last[A[i] - 1] 
      Arrow[i] = Aux 
      Length[i] = Length[Aux] + 1 
     Last[A[i]] = i 
     if Length[i] > Max: 
      ArgMax = i 
      Max = Length[i] 
    return (Arrow,ArgMax) 

(Arr,Start) = pre_process(A) 
Old = Arr[Start] 
ToRev = [] 
while 1: 
    ToRev.append(A[Start]) 
    if Old == Start: 
     break 
    Start = Old 
    New = Arr[Start] 
    Old = New 
ToRev.reverse() 
print ToRev  

Pythonizations欢迎

警告不起作用!!

0

好吧,这里的蟒蛇又一次尝试:

def popper(l): 
    listHolders = [] 
    pos = 0 
    while l: 
     appended = False 
     item = l.pop() 
     for holder in listHolders: 
      if item == holder[-1][0]-1: 
       appended = True 
       holder.append((item, pos)) 
     if not appended: 
      pos += 1 
      listHolders.append([(item, pos)]) 
    longest = [] 
    for holder in listHolders: 
     try: 
      if (holder[0][0] < longest[-1][0]) and (holder[0][1] > longest[-1][1]): 
       longest.extend(holder) 
     except: 
      pass 
     if len(holder) > len(longest): 
      longest = holder 
    longest.reverse() 
    return [x[0] for x in longest] 

样品输入和输出:

>>> demo = list(range(50)) 
>>> shuffle(demo) 
>>> demo 
[40, 19, 24, 5, 48, 36, 23, 43, 14, 35, 18, 21, 11, 7, 34, 16, 38, 25, 46, 27, 26, 29, 41, 8, 31, 1, 33, 2, 13, 6, 44, 22, 17, 
12, 39, 9, 49, 3, 42, 37, 30, 10, 47, 20, 4, 0, 28, 32, 45, 15] 
>>> popper(demo) 
[1, 2, 3, 4] 
>>> demo = [1,4,2,3,5,4,5,6,7,8,1,3,4,5,9,10,11] 
>>> popper(demo) 
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] 
>>> 
相关问题