python有排序列表吗?

Posted

技术标签:

【中文标题】python有排序列表吗?【英文标题】:Does python have a sorted list? 【发布时间】:2010-11-09 17:40:08 【问题描述】:

我的意思是一个结构:

x.push() 操作的 O(log n) 复杂度 O(log n) 复杂度找到一个元素 计算 list(x) 的 O(n) 复杂度将被排序

我还有一个关于 list(...).insert(...) 的性能的相关问题,现在是 here。

【问题讨论】:

memcpy 仍然是一个 O(n) 操作。我不确定 Python 如何实现列表确切地,但我敢打赌它们存储在连续的内存中(当然不是作为链表)。如果确实如此,那么您演示的使用bisect 的插入将具有复杂性O(n) 遗憾的是没有开箱即用。但是 Grant Jenk 的 sortedcontainers 库非常棒。 ***.com/a/22616929/284795 【参考方案1】:

您的 big-O 要求有什么特殊原因吗?或者你只是想让它快点? sortedcontainers 模块是纯 Python 且速度很快(就像在 blist 和 rbtree 等 fast-as-C 实现中一样)。

performance comparison 显示它的基准测试速度更快或与 blist 的排序列表类型相当。另请注意,rbtree、RBTree 和 PyAVL 提供排序的 dict 和 set 类型,但没有排序的列表类型。

如果性能是一项要求,请始终记住进行基准测试。一个使用 Big-O 表示法证明速度快的模块应该被怀疑,直到它也显示基准比较。

免责声明:我是 Python sortedcontainers 模块的作者。


安装:

pip install sortedcontainers

用法:

>>> from sortedcontainers import SortedList
>>> l = SortedList()
>>> l.update([0, 4, 1, 3, 2])
>>> l.index(3)
3
>>> l.add(5)
>>> l[-1]
5

【讨论】:

事实上,我将 sortedcontainers 与 bisect 进行了比较:0.0845024989976 for SortedList.add() vs 0.596589182518 for bisect.insort(),因此速度差异为 7 倍!我预计速度差距会随着列表长度而增加,因为 sortedcontainers 插入排序在 O(log n) 中工作,而 bisect.insort() 在 O(n) 中工作。 @gaborous 因为 bisect 仍然使用列表,所以插入仍然是O(n)【参考方案2】:

标准的 Python 列表不以任何形式排序。标准的 heapq 模块可用于将 O(log n) 附加到现有列表并删除 O(log n) 中最小的一个,但它不是您定义中的排序列表。

有多种 Python 平衡树实现可以满足您的要求,例如rbtree、RBTree 或 pyavl。

【讨论】:

+1 用于 rbtree,效果很好(但包含本机代码;不是纯 python,可能不太容易部署) sortedcontainers 是纯 Python 和 fast-as-C(如 rbtree)的性能比较。 “在你的定义中不是一个排序列表。”怎么样? heapq 只允许找到最小的元素; OP 要求一种可以在 O(log n) 中找到任何元素的结构,而堆不是。【参考方案3】:

虽然我还没有检查过基本 Python 列表操作的“大 O”速度, 在这种情况下,bisect 标准模块可能也值得一提:

import bisect
L = [0, 100]

bisect.insort(L, 50)
bisect.insort(L, 20)
bisect.insort(L, 21)

print L
## [0, 20, 21, 50, 100]

i = bisect.bisect(L, 20)
print L[i-1], L[i]
## 20, 21

PS。啊,对不起,bisect 在引用的问题中提到。不过,我认为如果这里有这些信息不会有太大的伤害)

PPS。还有CPython lists are actually arrays(不是,比如说,skiplists 等)。嗯,我想它们一定很简单,但对我来说,这个名字有点误导。


所以,如果我没记错的话,平分/列表速度可能是:

对于 push():最坏情况的 O(n) ; 对于搜索:如果我们认为数组索引的速度为 O(1),则搜索应该是 O(log(n)) 操作; 对于列表的创建:O(n) 应该是列表复制的速度,否则对于同一个列表是 O(1)

更新。在 cmets 讨论之后,让我在这里链接这些 SO 问题:How is Python's List Implemented 和 What is the runtime complexity of python list functions

【讨论】:

push() 应该在 O(log n) 中,因为列表已经排序。 也许我应该说"for an insert op"。无论如何,那是大约一年前的事了,所以现在我可以很容易地混淆或错过一些东西 您总是可以在 O(log n) 中将一个值插入到排序列表中,请参阅二分查找。 push() 被定义为插入操作。 是的。但是,虽然 finding 插入位置确实需要 O(log n) 个操作,但实际插入(即,将元素添加到数据结构中)可能取决于该结构(考虑在排序数组中插入元素)。而作为Python lists are actually arrays,这可能需要 O(n)。由于 cmets 的大小限制,我将从答案的文本中链接两个相关的 SO 问题(见上文)。 很好的论据。我不知道在 Python 中作为数组处理的列表。【参考方案4】:

虽然它没有(还)提供自定义搜索功能,但heapq 模块可能会满足您的需求。它使用常规列表实现堆队列。您必须编写自己的有效成员资格测试,利用队列的内部结构(可以在 O(log n) 中完成,我想说...)。有一个缺点:提取排序列表具有复杂性 O(n log n)

【讨论】:

很好,但很难一分为二。 堆中怎么会有 O(log n) 成员资格测试?如果您正在寻找值 x,如果您发现大于 x 的东西,您可以停止向下查看分支,但是对于 x 的随机值,它有 50% 的可能性位于叶子处,并且您可能无法修剪太多。 【参考方案5】:
import bisect

class sortedlist(list):
    '''just a list but with an insort (insert into sorted position)'''
    def insort(self, x):
        bisect.insort(self, x)

【讨论】:

在 bisect.insort() 中隐含的 insert() 是 O(n)【参考方案6】:

我会使用biscectsortedcontainers 模块。我真的没有经验,但我认为heapq 模块有效。它包含一个Heap Queue

【讨论】:

【参考方案7】:

在 Python 上实现您自己的排序列表可能并不难。下面是一个概念证明:

import bisect

class sortlist:
    def __init__(self, list):
        self.list = list
        self.sort()
    def sort(self):
        l = []
        for i in range(len(self.list)):
            bisect.insort(l, self.list[i])
        self.list = l
        self.len = i
    def insert(self, value):
        bisect.insort(self.list, value)
        self.len += 1
    def show(self):
        print self.list
    def search(self,value):
        left = bisect.bisect_left(self.list, value)
        if abs(self.list[min([left,self.len-1])] - value) >= abs(self.list[left-1] - value):
            return self.list[left-1]
        else:
            return self.list[left]

list = [101, 3, 10, 14, 23, 86, 44, 45, 45, 50, 66, 95, 17, 77, 79, 84, 85, 91, 73]
slist = sortlist(list)
slist.show()
slist.insert(99)
slist.show()
print slist.search(100000000)
print slist.search(0)
print slist.search(56.7)

=========结果============

[3、10、14、17、23、44、45、45、50、66、73、77、79、84、85、86、91、95、101]

[3、10、14、17、23、44、45、45、50、66、73、77、79、84、85、86、91、95、99、101]

101

3

50

【讨论】:

这还是基于insort,时间复杂度为O(n)。【参考方案8】:

AVL 树 [https://en.wikipedia.org/wiki/AVL_tree] 与中序遍历相结合将在所需的时间复杂度内解决此问题。

【讨论】:

【参考方案9】:

有趣的案例:如果您的列表 L 已经排序(例如,因为您按排序顺序附加它们),您可以从 O(log n) 中的快速 查找 中受益使用此方法的标准 Python 列表:

import bisect
def in_sorted_list(elem, sorted_list):
    i = bisect.bisect_left(sorted_list, elem)
    return i != len(sorted_list) and sorted_list[i] == elem
L = ["aaa", "bcd", "hello", "world", "zzz"]
print(in_sorted_list("hellu", L))       # False

更多详情请见this answer。

【讨论】:

以上是关于python有排序列表吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用python划分列表并按升序和降序排序?

Python“in”关键字对排序列表的效率

在python中,如何按元素的频率对列表进行排序

按频率排序列表

Python中对列表进行排序的方法都有哪些呢?

Python 3 列表排序与决胜局