Python数据结构-排序大O复杂性实现

Python数据结构-排序大O复杂性实现,python,sorting,big-o,Python,Sorting,Big O,我们都被告知,在许多语言中,对象的一般大小写排序是O(n*log(n))的流行理论极限 假设我们有一个列表: lst = [1,1,2,3,4,5,3,2,3,4,2,1,2,3] 在Python中,最近向我介绍了在字典上使用计数器(来自collections import Counter)的一些额外好处,其中键作为列表编号,值作为它们的出现计数器 coun = Counter(lst) print(coun) # ==> Counter({2: 4, 3: 4, 1: 3, 4: 2

我们都被告知,在许多语言中,对象的一般大小写排序是O(n*log(n))的流行理论极限

假设我们有一个列表:

lst = [1,1,2,3,4,5,3,2,3,4,2,1,2,3]

在Python中,最近向我介绍了在字典上使用计数器(
来自collections import Counter
)的一些额外好处,其中键作为列表编号,值作为它们的出现计数器

coun = Counter(lst) 
print(coun) # ==> Counter({2: 4, 3: 4, 1: 3, 4: 2, 5: 1})

多次()表明计数器的构造需要O(n),与标准dict不同,计数器()有一些额外的空间开销来存储每个元素的频率

当您尝试使用计数器时,它通常会按排序顺序返回输出:
.items()
.keys()
。也许为了方便起见,它在给出结果之前应用了一个快速的O(logn)排序,但当您在简单遍历中使用它时,它听起来出乎意料地糟糕:

for i in range(len(lst)):
    if lst[i] not in coun.keys():
        print("element", lst[i], "not found!")
您自然会期望上面的复杂性是O(n),就像在标准字典中一样(检查是否存在是O(1)除以n个循环)

因此,在不选择代码的情况下,我们假设
lst[i]not in conu.keys()
是以O(1)复杂度实现的,使用了一些空间开销

理论上是否可能在计数器构造过程中,这种额外的(对于真正大的和唯一的列表来说可能会非常大)空间开销使我们在中小型列表(长度<1000)中获得优势,从而以使用额外空间为代价获得O(n)排序优势。 如果以上是可能的,我假设在幕后有一种机制,当内存占用超过某个定义的值(如1Mb)并且
lst[I]不在conu.keys()中时,
将变成O(logn),该机制将停止对每个元素进行计数并将它们放入正确的排序顺序

在这里大声思考一下,事实上,我们正在处理的很多列表实际上都不到1000个元素

事后思考1: 另一方面,当n排序算法的O(n*log(n))下限仅适用于可以通过相互比较对任意对象进行排序的算法时,您可能不会太在意O(n)vs O(nlogn)。如果您知道您的数据来自有限的域,则可以使用更高效的算法。例如,如果值都是小整数,则可以使用a在O(n)时间内对数据进行有效排序

下面的示例可以对仅包含域0-5中整数的序列进行排序,如您的示例所示

def sort_0_to_5(data):
    counts = [0, 0, 0, 0, 0, 0]
    for val in data:
        counts[val] += 1
    return [val for val in range(len(counts)) for _ in range(counts[val])]
这在O(n)时间内运行,并且只使用恒定的空间。这是一个非常基本的计数排序,更高级的版本可以对任意对象进行排序,只要它们在域中有整数键。(您只需要在数据上进行两次额外的传递,以进行累积计数,然后以正确的顺序建立输出。)

更复杂的算法,如基数排序,可以在准线性时间内处理更大的域。但是,您需要考虑时间的方式变得很棘手,因为一旦域开始与数据集的大小相比较,处理域大小的代码部分就变得越不“恒定”。例如,基数排序需要O(n*log(k))时间,其中k是域的大小


然而,我要指出的是,即使您能够找到一种比标准比较排序具有更好的时间复杂度的排序算法,但这实际上并不意味着它比实际数据更快。除非数据集的大小很大,否则从渐近分析中排除的常数项可能非常重要。您可能会发现,一个非常好地实现的O(n*log(n))排序(就像Python的
排序后的排序
)比手工编写的O(n)排序性能更好。

“而且与标准dict不同,计数器()有一些额外的空间开销来存储每个元素的频率。”,
计数器
不维护已排序的键。你的问题基于不真实的假设。您使用的是哪个Python版本?在最新版本中,dicts从集合导入计数器myList=[1,1,2,3,4,5,3,2,3,4,2,1,2,3]print(Counter(myList))#Counter({2:4,3:4,1:3,4,4:2,5:1})print(Counter(myList).items())dictu items([(1,3)、(2,4)、(4,2)、(5,1)])print(Counter(Counter(myList).keys())dictu键([1,2,3,4,5])你说得对,我只是有一个糟糕的初始数据示例。如果我改变原始数据的顺序,keys()将不再返回排序列表?Dict键保持插入顺序是的,听起来好像是我忽略了这个例子,让我感觉它们被排序了。非常好的想法值得考虑。非常感谢。