在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。注意,如果出于某种原因您已经知道范围,则可以跳过此步骤元素\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]
>>>