我正在尝试查找列表是否是严格递增的序列(如果从列表中删除了1个元素且仅删除了1个元素)。这对于某些列表有效,但是当列表之间没有明显区别时,则不适用于其他列表。对于大型列表,它超过了执行时间限制。这是我的代码:
def almostIncreasingSequence(sequence):
for i in range(len(sequence)):
new_seq = sequence.copy()
del new_seq[i]
if all(i < j for i, j in zip(new_seq, new_seq[1:])):
output = True
else:
output = False
return output
我正在创建输入列表的副本。然后,我要删除元素i
,然后根据列表是否严格增加顺序返回True
或False
。我在for循环中创建列表的副本,以确保仅删除一个元素。这些是一些测试运行,其中代码未返回适当的值:
Input:
sequence: [10, 1, 2, 3, 4, 5]
Output: false
Expected Output: true
Input:
sequence: [1, 2, 5, 3, 5]
Output: false
Expected Output: true
在此测试用例中,代码超出了执行时间限制:
Input:
sequence: [-9996, -9995, -9994, -9993, -9991, -9989, -9987, -9986, -9985, -9983, -9982, -9980, -9978, -9977, -9976, -9975, -9974, -9972, -9968, -9966, -9965, -9961, -9957, -9956, -9955, -9954, -9952, -9948, -9942, -9939, -9938, -9936, -9935, -9932, -9931, -9927, -9925, -9923, -9922, -9921, -9920, -9919, -9918, -9908, -9905, -9902, -9901, -9900, -9899, -9897, -9896, -9894, -9888, -9886, -9880, -9878, -9877, -9876, -9874, -9872, -9871, -9870, -9869, -9868, -9867, -9865, -9857, -9856, -9855, -9854, -9853, -9852, -9851, -9849, -9848, -9846, -9845, -9843, -9842, -9841, -9840, -9837, -9834, -9828, -9826, -9824, -9823, -9820, -9816, -9814, -9812, -9811, -9810, -9809, -9807, -9806, -9804, -9803, -9801, -9800]
Output: undefined
Expected Output: false
答案 0 :(得分:2)
您的循环将始终在每次迭代中覆盖(返回False或True)先前的结果。因此,它实际上“忘记”了先前迭代中的任何违规。只有最后一次迭代才能确定输出,因此您的算法是错误的。
其次,即使您要修复该错误(如果未对新列表进行排序,则通过退出循环),该算法也会表示 O(n²),因为在每个迭代中:
.copy()
del
,要求转移其后的所有值new_seq[1:]
zip
被调用,创建另一个列表all
被调用,迭代新列表所有这些动作都在破坏表演。
相反,您应该只对列表执行一个迭代,并且可以解决此问题,而无需进行任何嵌套的迭代。一个提示是,您实际上不必执行删除元素。您只需检查情况是否已删除。
高效的算法只需要查看三个值:当前迭代的值和前面的两个值。如果这三个顺序不正确,则可以确定应删除哪一个,而仅在这三个变量中表示该删除(请保持列表不变):
def almostIncreasingSequence(sequence):
beforePrev = prev = float('-inf')
allowExceptions = True
for curr in sequence:
if curr <= prev: # Order is not maintained:
if not allowExceptions: # It's not the first time
return False
allowExceptions = False
# Decide whether to skip the current or previous value
if curr > beforePrev:
prev = curr;
else: # Normal case: keep track of two preceding values
beforePrev, prev = prev, curr
return True
遍历列表时,三个列表值分别保存在单独的存储器中:curr
,prev
和beforePrev
。后两个滞后于curr
值。因此,在每次迭代中,curr
获取当前列表值,并且在迭代结束时,我们将值移位:beforePrev ← prev ← curr
。因此,从本质上讲,这三个值对应于最近访问的三个值-至少在列表中没有异常时。
因此,让我们说在某个时刻您发现异常:您发现curr <= prev
的位置,即序列未严格增加;那么显然必须删除其中一个值:应该删除curr
或prev
,以希望列表可以被“修复”。
算法可能已经确定必须在更早的迭代中删除一个值(allowExceptions
为False
):但是我们只允许从列表中删除一个值,因此在这种情况下,我们认为没有解决方案,并返回False
但是,如果这是我们第一次遇到这种情况,那么我们应该决定是将curr
还是prev
标识为违规值。我们可以假设beforePrev
和prev
的顺序正确(我们在前面的迭代中验证了这一点),因此,beforePrev
{{ 1}}和prev
:是否为curr
。
如果curr > beforePrev
,则将这两个相对放置在相对良好的位置,因此,如果我们从这两个之间删除curr > beforePrev
,则可以将该列表的那一部分恢复为递增形式订购。
如果prev
,则它们彼此之间的顺序不佳,因此我们应删除curr <= beforePrev
,以恢复递增的顺序。
为了节省时间,我们实际上将不会执行从列表中删除所选项目:我们只会将该删除结果应用于变量curr
,{{1} }和beforePrev
,以便在下一次迭代中它们具有与列表中已删除一项相同的值:
在删除元素时,prev
在循环的下一次迭代中应保持不变:因此,我们不会像通常那样触摸它的值。
如果我们删除curr
,那么新的beforePrev
就是现在的prev
。如果我们删除prev
,则无事可做:curr
在下一次迭代中也保持不变,并且curr
在下一次迭代开始时将以任何方式获得其新值。
因为我们“删除”了一个项目,所以我们也将prev
设置为curr
,以便我们知道我们不允许进行第二次删除。
仍然要说的是,在第一次迭代中,我们实际上并没有allowExceptions
和False
,因此我们将它们设置为-infinity,这样在第一次迭代中就不会做出决定。删除。
答案 1 :(得分:1)
您将返回最后一个检查的值,即,当您删除列表中的最后一个项目时。将其更改为在第一个匹配项中返回True
,如果没有匹配项则返回False
def almostIncreasingSequence(sequence):
for i in range(len(sequence)):
new_seq = sequence.copy()
del new_seq[i]
if all(i < j for i, j in zip(new_seq, new_seq[1:])):
return True
return False