如何优化列表操作? (CodeFights)

时间:2018-03-20 23:45:19

标签: python list optimization

所以我有一个在小清单上表现得很好的功能。它的功能是检查从序列中删除一个元素是否会使序列严格增加序列:

def almostIncreasingSequence(sequence):
    length = len(sequence)
    for i in range(1, length):
        newSequence = sequence[:i-1] + sequence[i:]
        if checkIfSorted(newSequence, length):
            return True
    return checkIfSorted(sequence[:length-1], length)

def checkIfSorted(sequence, length):
    for i in range(1, length - 1):
        if sequence[i-1] >= sequence[i]:
            return False
    return True

但我需要它才能处理长达100,000个元素的列表。我可以通过哪种优化来加快工作速度?现在,它在10万个名单上非常缓慢,每秒通过几千个元素。

3 个答案:

答案 0 :(得分:1)

我写了another answer on this site回答了几乎与你相同的问题,但我的目的是检查从序列中删除最多一个元素是否会严格增加。这可能就是你的意思 - 似乎没有实际的区别。看来你想要我的第二个解决方案,复制到这里。

def first_bad_pair(sequence, k):
    """Return the first index of a pair of elements in sequence[]
    for indices k-1, k+1, k+2, k+3, ... where the earlier element is
    not less than the later element. If no such pair exists, return -1."""
    if 0 < k < len(sequence) - 1:
        if sequence[k-1] >= sequence[k+1]:
            return k-1
    for i in range(k+1, len(sequence)-1):
        if sequence[i] >= sequence[i+1]:
            return i
    return -1

def almostIncreasingSequence(sequence):
    """Return whether it is possible to obtain a strictly increasing
    sequence by removing no more than one element from the array."""
    j = first_bad_pair(sequence, -1)
    if j == -1:
        return True  # List is increasing
    if first_bad_pair(sequence, j) == -1:
        return True  # Deleting earlier element makes increasing
    if first_bad_pair(sequence, j+1) == -1:
        return True  # Deleting later element makes increasing
    return False  # Deleting either does not make increasing

你的代码很慢,因为它像我的第一个解决方案一样,通过连接切片来创建新的序列。这几乎复制了整个序列,这样做很多次会减慢代码速度。上面的代码避免了通过使检查序列的例程复杂化来查看它是否严格增加。查看我的其他链接答案以获取更多详细信息。

答案 1 :(得分:1)

我的解决方案:

def is_almost_increasing(x):
    lastx = x[0]  # value to use in the next comparison
    found_one = False
    for i in range(1, len(x)):
        if x[i] <= lastx:
            if found_one:
                return False
            found_one = True
            if i > 1 and x[i] <= x[i-2]:  # i > 1 in case the first comparison failed
                break
        lastx = x[i]
    return True


print('\nThese should be True.')
print(is_almost_increasing([1]))
print(is_almost_increasing([1, 2]))
print(is_almost_increasing([1, 2, 3]))
print(is_almost_increasing([1, 3, 2]))
print(is_almost_increasing([10, 1, 2, 3, 4, 5]))
print(is_almost_increasing([0, -2, 5, 6]))
print(is_almost_increasing([1, 1]))
print(is_almost_increasing([1, 2, 3, 4, 3, 6]))
print(is_almost_increasing([1, 2, 3, 4, 99, 5, 6]))
print(is_almost_increasing([1, 2, 2, 3]))

print('\nThese should be False.')
print(is_almost_increasing([1, 3, 2, 1]))
print(is_almost_increasing([3, 2, 1]))
print(is_almost_increasing([1, 1, 1]))

这与Rory Daulton非常相似,但略短。我从他提供的链接中借用了他的测试代码,所以感谢他。关键是你不想建立很多次要列表,这是低效的。为了实现效率的极大提升,您几乎总是需要找到更好的算法。

这里的两个复杂因素是(1)当第一个元素未通过测试时该怎么办? (2)当你发现一个元素不按顺序时,你会丢弃那个元素还是之前的元素?评论说明了这一点。

答案 2 :(得分:1)

好的,我知道这篇文章已经有了答案。但我只想提供相同问题的代码。假设您有两个列表lis1和lis2,如下所示: -

test_lis1 = [1, 2, 3, 4, 5, 2, 6]
test_lis2 = [1, 2, 3, 1, 4, 5, 1]
test_lis3 = [7, 1, 2, 3, 4, 5, 6]

现在,我猜你的问题是你想知道这个列表是否可以通过仅删除一个元素转换为严格增加的子序列。如果我们需要删除两个,那么答案就是否。

这是功能: -

def remove_one_only(lis):
    len_lis = len(lis)
    i = 0
    j = 1
    rem_count = 0
    flag = False
    while j <= len_lis - 1:
        if lis[i] > lis[j]:
            rem_count += 1
            if rem_count == 2:
                break
            if i > 0:
                j += 1
                continue
        i = j
        j += 1
    if rem_count == 1:
        flag = True
    return flag

如果只删除一个元素可以将列表转换为严格增加的列表并且它在O(n)中运行,则此函数将告诉您是或否的答案。这是你能得到的最快的。