【问题标题】:Is a.insert(0,x) an o(n) function? Is a.append an O(1) function? Pythona.insert(0,x) 是 o(n) 函数吗? a.append 是一个 O(1) 函数吗? Python
【发布时间】:2012-07-05 10:09:56
【问题描述】:

我正在尝试将数组中的偶数移动到数组的前面,将奇数移动到数组的后面。问题要求在线性算法中执行此操作并就地执行此操作。

我想出了这个:

 def sort(a):
    for i in range(0,len(a)-1):
        if a[i]%2==0:
            a.insert(0,a.pop(i))
    return a

问题是,有人告诉我,从技术上讲,a.insert 是一个 o(n) 函数,所以从技术上讲,这将被视为非线性算法(当包括函数的 for i in range 部分时)。由于提出这个问题的论坛已有几个月的历史,我无法要求解释。

基本上,我相信他说的是“技术上”,因为由于这会将其插入到前面,因此它不会检查数组中的另外 N 个元素,因此使其在 O(n) 而不是 O(n ^2)。这是一个正确的评估吗?

另外,论坛上有人用a.append修改了上面的,改成找奇数。没有人回答,所以我想知道, a.append 不是 o(n) 函数,因为它将它移动到最后吗?是o(1)吗?

感谢您的解释和澄清!

【问题讨论】:

  • O(n) linear
  • 糟糕。抱歉,我的意思是当您还考虑 for i in range 我已经编辑了我的问题时,它不是线性的。
  • lst = [i for i in lst if not i % 2] + [i for i in lst if i % 2]

标签: python list insertion


【解决方案1】:

insert 在列表的第 0 个索引处需要移动每个其他元素,这使其成为 O(N) 操作。但是,如果您使用deque,则此操作为 O(1)。

append 是一个摊销的 O(1) 操作,因为它只需要将项目添加到列表的末尾并且不进行移动。有时列表需要增长,所以它并不总是 O(1) 操作。

【讨论】:

  • 实际上它只是一个 amortized O(1) 用于追加,原因很明显(我们有时必须增加列表)。
  • @Voo 好的,然后我会将其更改为平均情况并包含该信息。
  • 请注意,平均案例复杂度与摊销复杂度不同。前者是具有“平均”(但不是全部)数据/参数的某种复杂性,后者适用于所有输入,并表示如果您经常重复操作,则每次操作获得的复杂性。也就是说,一个平均情况的 O(1) 追加可能还需要 O(n!) 与一些列表(例如排序的列表),当你重复追加时,一个摊销的 O(1) 追加是 O(1),看看总时间,以及平均时间 - 无论列表中有什么。
【解决方案2】:

这是正确的 - 在 Python 标准列表的前面插入是 O(n)。 Python 列表是作为数组实现的,因此在列表的前面插入一些东西需要将整个内容转移到一个位置。另一方面,追加不需要任何移位,因此摊销 O(1)。

但是请注意,a.pop(i) 也是一个 O(n) 操作,因为它需要将弹出项目之后的所有内容移动到一个位置。因此,简单地修改您的代码以使用append() 而不是insert() 仍然不会产生线性算法。

线性时间算法不会使用pop(),而是会执行诸如交换元素之类的操作,这样就不必修改列表的其余部分。例如,考虑一下:

def even_to_front(a_list):
    next_even = 0
    for idx in xrange(len(a_list)):
        if not a_list[idx] % 2:
            # a_list[idx] is even, so swap it towards the front
            a_list[idx], a_list[next_even] = a_list[next_even], a_list[idx]
            next_even += 1

【讨论】:

  • 嘿,很好的答案。感谢您教我它将所有内容都向右移动 1。我没有意识到这就是它在技术上适用于其他 n 个元素的原因!
【解决方案3】:

这里是如何在没有追加/插入或出列的情况下完成的

def sort(L):
    i, j = 0, len(L)-1
    while i<j:
        # point i to the next odd number from the start
        while i<j and not L[i]%2: i+=1
        # point j to the next even number from the end
        while i<j and L[j]%2: j-=1
        L[i],L[j] = L[j],L[i]    

【讨论】:

    【解决方案4】:

    查看table of complexity

    • 插入 - O(n)
    • 追加 - O(1)(列表过度分配)

    【讨论】:

    • 感谢那张桌子。在我前进的过程中,这绝对是无价之宝!
    【解决方案5】:

    每次您从列表中pop 元素时,您都必须复制列表的尾随部分以将其移动到一个索引上以填充被移除元素留下的空洞。弹出元素与列表尾部之间的距离是线性的。

    每次将元素insert 放入列表时,您都必须复制列表的尾随部分以将其移动到一个索引上以创建插入新元素的位置。这与您插入元素的位置和列表尾部之间的距离成线性关系。

    如果你使用collections.deque,你可以在O(1)时间前后追加和弹出。但是,从中间删除一个元素仍然是线性的(我认为您必须自己编写)。

    【讨论】:

      猜你喜欢
      • 2021-11-20
      • 1970-01-01
      • 1970-01-01
      • 2011-08-15
      • 1970-01-01
      • 2016-04-01
      • 1970-01-01
      • 2021-02-27
      • 2017-12-30
      相关资源
      最近更新 更多