在python中,如何有效地查找列表中不一定相邻的最大连续数字集?

在python中,如何有效地查找列表中不一定相邻的最大连续数字集?,python,arrays,algorithm,numpy,dynamic-programming,Python,Arrays,Algorithm,Numpy,Dynamic Programming,例如,如果我有一个列表 [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的解决方案,因为算法才是最重要的 谢谢。这应该可以做到(而且是O(n)): 对于任何起始编号,这适用于: result = [] for x in mylist: matched = Fa

例如,如果我有一个列表

[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的解决方案,因为算法才是最重要的

谢谢。

这应该可以做到(而且是O(n)):

对于任何起始编号,这适用于:

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:]
您可以使用

这是测试结果

>>> 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]
复杂性的顺序是O(nlogn)

wiki链接中有一条注释,他们声称可以通过依赖

来实现O(n.loglogn)不是那么聪明,不是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)

使用一个修改过的软件怎么样?正如JanneKarila指出的,解决方案不是O(n)。它使用基数排序,wikipedia称,对于数字为k或更少的n个键,基数排序的效率为O(k·n)。

只有当你知道我们要处理的数字范围时,这才有效,所以这将是第一步

  • 查看起始列表中的每个元素,找出最低、
    l
    和最高、
    h
    编号。在这种情况下,
    l
    为1,
    h
    为11。注意,如果出于某种原因您已经知道范围,则可以跳过此步骤

  • 创建一个范围大小的结果列表,并将每个元素设置为null

  • 查看列表中的每个元素,如果需要,将它们添加到结果列表的适当位置。即,元素为4,将4添加到位置4的结果列表中<代码>结果[元素]=开始列表[元素]。如果你想的话,你可以扔掉重复的,它们只会被覆盖

  • 遍历结果列表以查找没有任何空值的最长序列。保留一个
    元素\u计数器
    ,以了解我们正在查看的结果列表中的元素。将
    curr\u start\u元素设置为当前序列的开始元素,并将当前序列的长度设置为
    curr\u len
    。同时保留一个
    最长的\u开始\u元素
    和一个'longest\u len',该元素将以零开始,并在列表中移动时更新

  • 返回从
    longest\u start\u元素开始并取
    longest\u 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]
    

    如果结果确实必须是连续递增整数的子序列,而不仅仅是递增整数,那么在确定哪个子序列最长之前,不需要记住整个连续子序列,只需要记住每个子序列的起始值和结束值。所以你可以这样做:

    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]
    )运行此程序,我会得到:

    如果我在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)。

    这里有一个简单的单通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)
    

    警告:这是一种欺骗的方式(也就是我使用python…)


    您需要最大连续和():

    def msum2(a):
    边界,s,t,j=(0,0),-float(‘无穷大’),0,0
    对于范围内的i(len(a)):
    t=t+a[i]
    如果t>s:bounds,s=(j,i+1),t
    如果t<0:t,j=0,i+1
    返回(s,界限)
    
    这是动态规划的一个例子,即使序列不是从第一个元素开始,O(N)

    O(N)解决方案仍然有效

    如果len(A)=0,则警告无效


    欢迎你做pythonization

    好的,下面是python的另一个尝试:

    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]
    

    为什么不
    [1,2,3,4,5,6,7,8,9,10,11]
    。我看没有理由不包括这些数字,因为它们不必相邻。对不起,我错了。感谢您的更正。最长的连续序列是否可以从1以外的数字开始?算法是否同时向前和向后工作?只向前,不需要向后。这将找到第一个,而不是最大的,从1开始<代码>[2,3,4,5,1,2]
    哇,真聪明,谢谢。但是对于
    [1,2,3,11,12,13,14]
    呢?这个算法会返回
    [1,2,3]
    ?为什么不检查代码呢?。你怎么能第一次订阅
    y
    ?(
    TypeError:'int'对象不可订阅
    )第一个代码示例返回一个空列表,第二个代码示例在
    行中引发
    TypeError:'int'对象不可订阅
    ,如果y[0]==x
    。同时
    False
    应大写,但是在运行之前我已经修正了这个问题。结果不是必须是连续整数吗?@srgerg,只需检查Serdalis和Chi Zeng回答的上述问题。这不是最大的升序,而是最大的连续升序。实际上没有*O*(n)实现:-)这是O(n^2),这是我的。需要考虑一种不同的方法。@Abhijit:有,看看Raymond Hettingers的方法。步骤4在结果列表上迭代n次,所以这不是O(n)。@jknupp不,你只需要经历一次。这与从列表中查找最大值相同。除非它找到列表中最长的序列。假设list=
    [1,2,3,null,5,6,7,8,null,10]
    我看到
    [1,2,3]
    的长度是3,所以我保存了开始索引。然后查看
    [5,6,7,8]
    是长度4,因此更新最长索引/长度变量<代码>[8]
    不会更改它。一个循环,发现最长。O(n)中的n表示输入列表的大小。值的范围可以大得多,并且与列表的长度无关。@JanneKarila m
    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)
    
    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])
    
    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)
    
    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     
    
    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]
    >>>