转:Python的sort()

本文详细探讨了Python内置排序方法sort()的使用,包括如何通过自定义比较函数实现个性化排序,并揭示了sort()采用的排序算法。文章通过实例演示了使用自定义函数对包含特定类型数据(如字符串及其权重)的列表进行排序,进而探究了sort()内部所使用的排序策略,最终得出结论:sort()采用的是二分法插入排序。此外,文章还通过实验展示了sort()在不同情况下表现的算法特性,如对已排序数组的处理方式,以及混合排序算法的实现,旨在帮助读者更深入地理解Python排序机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前学习第九章的排序小结的时候,对sort()排序方法不理解,因为括号里面带了自定义的比较函数。

后来查手册,才发现sort()里面本来就带了这样的参数。能够自定义比较方法,确实很灵活。

不仅如此,在网上查到一个博客,作者不单停留在这表面,还查究了sort()的排序算法,确实有意思。

全文抄录如下:

 

http://blog.donews.com/maverick/archive/2006/07/09/951101.aspx

 

学习笔记:Python的排序

 

Python语言内置了sort方法,可以很方便地对某个List进行排序:
L = [6, 5, 1, 3, 4, 2]
L.sort()
print L

---------- Run Python Program ----------
[1, 2, 3, 4, 5, 6]

某些时候,我们希望按照自己定义的排序规则来排序(例如,按关键词的权重排序,按人的年龄排序,等等)。在Java语言中,我们可以自定义Comparator来实现,Python中也提供了类似的办法。

若List中每个元素都是2-tuple,tuple中第一个元素为String类型的keyword,第二个元素为该字符串对应的权重(int类型),希望按照权重排序(从高到低),则可以这样:
def my_cmp(E1, E2):
    return -cmp(E1[1], E2[1])    #compare weight of each 2-tuple
                    #return the negative result of built-in cmp function
                    #thus we get the descend order

L = [('a', 0), ('b', 1), ('c', 2), ('d', 3)]
L.sort(my_cmp)
print L

---------- Run Python Program ----------
[('d', 3), ('c', 2), ('b', 1), ('a', 0)]

正因为可以自定义cmp方法,我们不妨探究一下,built-in的sort方法,到底是采用的哪一种排序算法:
from random import shuffle

def my_cmp(E1, E2):
    print 'E1:', E1, 'E2:', E2
    return cmp(E1, E2)

L = range(0, 10)
shuffle(L)
print L
L.sort(my_cmp)

---------- Run Python Program ----------
[5, 3, 7, 6, 2, 8, 9, 4, 1, 0]
E1: 3 E2: 5
E1: 7 E2: 3
E1: 7 E2: 5
E1: 6 E2: 5
E1: 6 E2: 7
E1: 2 E2: 6
E1: 2 E2: 5
E1: 2 E2: 3
E1: 8 E2: 5
E1: 8 E2: 7
E1: 9 E2: 6
E1: 9 E2: 8
E1: 4 E2: 6
E1: 4 E2: 3
E1: 4 E2: 5
E1: 1 E2: 6
E1: 1 E2: 4
E1: 1 E2: 3
E1: 1 E2: 2
E1: 0 E2: 5
E1: 0 E2: 3
E1: 0 E2: 2
E1: 0 E2: 1

可以看到,每次调用my_cmp,E1依次是List中的第2、3、4……直到最后一个元素,可以肯定sort不是采用的分治法(devide-and-conqure)
看前三次调用,可以发现sort采用的是典型的插入排序——也就是说,将新元素插入已排序部分的合适位置,查找位置时采用的似乎是从后向前线形探察的办法。从元素6开始,新元素不再直接与已排序部分的最后元素比较,而是先与中间值比较,采用二分检索探察合适位置,这样,可以把时间代价从n缩小到log(n)。
至此,我们可以认为,built-in的sort方法,采用的是“二分法插入排序”(binary insertion)的算法。

为了检测这个结论,可以输入一个已排序的数组,看看结果。
L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
L.sort(my_cmp)

---------- Run Python Program ----------
E1: 1 E2: 0
E1: 2 E2: 1
E1: 3 E2: 2
E1: 4 E2: 3
E1: 5 E2: 4
E1: 6 E2: 5
E1: 7 E2: 6
E1: 8 E2: 7
E1: 9 E2: 8
E1: 10 E2: 9

结果发现,比较的次数非常少,插入每个元素的时候,只会与它之前紧邻的元素比较,而不是二分检索。这真是个有意思的现象。

改一改程序
L = [0, 1, 2, 3, 4, 5, 6, 8, 7, 9, 10]
L.sort(my_cmp)

---------- Run Python Program ----------
E1: 1 E2: 0
E1: 2 E2: 1
E1: 3 E2: 2
E1: 4 E2: 3
E1: 5 E2: 4
E1: 6 E2: 5
E1: 8 E2: 6
E1: 7 E2: 8
E1: 7 E2: 4
E1: 7 E2: 6
E1: 7 E2: 8
E1: 9 E2: 4
E1: 9 E2: 7
E1: 9 E2: 8
E1: 10 E2: 5
E1: 10 E2: 8
E1: 10 E2: 9

可以看到,在数字8以前,List中的元素都是有序的,于是sort算法也只比较欲插入元素和已排序序列的最后一个元素;一旦发现输入序列不是自然有序之后,就采用二分插入排序算法。
这样的混合排序算法,相对单纯的插入排序,保证了最好条件下时间代价最低,也减小了一般情况下的时间代价。

p.s.
写完之后查阅Python的文档,发现sort采用的是混合(hybrid)排序,规模小的时候采用binary insertion,规模大的时候采用samplesort(据说是quicksort的一个变种,我没接触过,汗....)

<think>好的,我现在需要帮用户解决如何用Python实现类似C++中std::sort的排序函数的问题。首先,我得回想一下C++的std::sort是怎么工作的。std::sort默认使用的是升序排序,并且使用的是比较型的排序算法,通常是快速排序、堆排序和插入排序的结合,也就是内省排序。那Python中的排序方法,比如list的sort()方法和sorted()函数,它们默认是Timsort算法,属于稳定排序,但用户可能想要更接近std::sort的行为,比如不稳定排序或者自定义比较函数。 接下来,我得考虑用户的具体需求。用户可能希望用Python实现类似std::sort的功能,可能指的是以下几个方面:一是原地排序,类似std::sort对容器进行原地排序;二是支持自定义比较函数;三是排序的不稳定性,因为std::sort是不稳定的,而Python的Timsort是稳定的。不过,Python的排序默认稳定,这可能和用户的需求不同。 Python中list.sort()是原地排序,类似于C++中的std::sort对vector的排序。所以这一点可能已经满足。但比较函数方面,C++中的std::sort可以接受一个自定义的比较函数或函数对象,而Pythonsort()方法使用key参数来指定排序的关键字,或者可以使用cmp参数,但cmp在Python3中已经被移除了,需要通过functools.cmp_to_key来换。 比如,C++中可能这样写比较函数: ```cpp std::sort(vec.begin(), vec.end(), [](int a, int b) { return a > b; }); ``` 而Python中要实现降序排序的话,可以用reverse=True,或者使用key参数。但如果用户需要自定义比较逻辑,比如先按奇偶再按大小,可能需要用到cmp_to_key。 所以,用户可能需要知道如何在Python中处理自定义比较函数,类似于C++中的方式。此外,Python的Timsort是稳定的,而std::sort是不稳定的,如果用户需要不稳定的排序,可能需要自己实现或者改变排序策略,但这在Python中可能不太常见。 另外,用户提供的引用中有提到C++的std::ranges::for_each,constexpr函数的限制,以及std::package_task,但这些可能和当前问题关联不大。不过引用[2]提到在constexpr函数中不能用std::swap和std::sort,这可能说明用户对C++的某些限制有了解,但Python中不存在这样的问题。引用[4]提到用户希望将C++ vector的数据传递到numpy数组,这可能和用户当前的问题无关,但可能用户有跨语言交互的需求,但当前问题仅关于Python实现排序。 所以,核心问题是如何在Python中实现类似于C++的std::sort的功能,包括原地排序、自定义比较函数、可能的不稳定排序。但Python的排序默认是稳定的,所以不稳定这一点可能无法直接实现,除非用户自己实现另一种算法。 首先,原地排序:Python中的list.sort()是原地排序,等同于std::sort对容器的排序。 其次,自定义比较函数:Python中使用key参数或者通过cmp_to_key来处理比较函数。例如,如果要按照绝对值排序,可以使用key=abs。但如果需要更复杂的比较逻辑,如比较两个元素的某种关系,可能需要将比较函数换为key函数,或者使用cmp_to_key。例如,C++中的比较函数返回a < b则升序,而Python中的cmp_to_key需要返回-1、0、1。例如,实现降序排序: 在C++中: ```cpp std::sort(vec.begin(), vec.end(), [](int a, int b) { return a > b; }); ``` 在Python中: ```python from functools import cmp_to_key def compare(a, b): if a < b: return 1 # 表示a应该排在b后面 elif a > b: return -1 # 表示a应该排在b前面 else: return 0 lst.sort(key=cmp_to_key(compare)) ``` 或者更简单的方式,使用reverse参数: ```python lst.sort(reverse=True) ``` 但如果是更复杂的比较逻辑,比如先按奇偶性再按数值大小排序,可能需要自定义比较函数。例如,奇偶数先偶后奇,然后数值大的在前: 比较函数可能如下: ```python def compare(a, b): if a % 2 == b % 2: return b - a # 同奇偶时,数值大的排前面 else: return -1 if a % 2 == 0 else 1 # 偶数排前面 ``` 然后使用cmp_to_key换后作为key参数。 但需要注意,使用cmp_to_key会比使用key函数效率低,因为每次比较都需要调用函数,而key函数每个元素只计算一次。所以推荐尽可能使用key函数。 关于不稳定排序,Python的Timsort是稳定的,而std::sort是不稳定的。如果用户需要不稳定的排序,可能需要自己实现快速排序或其他不稳定算法,或者修改原数据以破坏稳定性。不过这可能比较复杂,通常用户可能不需要特别处理,除非在特定场景下需要。 因此,总结步骤: 1. 使用list.sort()进行原地排序,类似于std::sort的原地性。 2. 对于自定义比较逻辑,使用key参数或cmp_to_key换比较函数。 3. 如果不需要稳定性,可以忽略,因为Python本身不提供不稳定的内置排序,但大多数情况下稳定性不影响结果,除非有相同键值且需要保持原顺序的情况。 示例代码: 原地排序: ```python arr = [3, 1, 4, 1, 5, 9, 2, 6] arr.sort() # 默认升序,类似std::sort的默认行为 ``` 自定义比较函数(降序): ```python arr.sort(reverse=True) # 降序,简单情况 ``` 复杂比较函数: ```python from functools import cmp_to_key def compare(a, b): # 先按奇偶,偶数在前,奇数在后;同奇偶时数值升序 if a % 2 == b % 2: return a - b # 升序 else: return -1 if a % 2 == 0 else 1 arr.sort(key=cmp_to_key(compare)) ``` 如果需要不稳定的排序,可能需要自己实现,比如快速排序: ```python def quick_sort(arr): if len(arr) <= 1: return arr pivot = arr[len(arr) // 2] left = [x for x in arr if x < pivot] middle = [x for x in arr if x == pivot] right = [x for x in arr if x > pivot] return quick_sort(left) + middle + quick_sort(right) arr = [3, 1, 4, 1, 5, 9, 2, 6] arr = quick_sort(arr) # 非原地,但可以改为原地版本 ``` 但这样实现的快速排序和std::sort的内省排序不同,且不是原地排序。如果需要原地排序的快速排序,实现起来会更复杂。 另外,用户可能关心性能问题,Python的内置排序已经高度优化,通常比自己实现的排序要快,所以除非有特殊需求,否则建议使用内置的sort方法。 现在需要检查是否有其他引用内容相关。引用[1]提到std::ranges::for_each,可能与问题无关;引用[2]提到C++中constexpr函数不能用std::swap和std::sort,这可能在Python中不是问题;引用[4]是关于C++和numpy的交互,可能与当前问题无关。所以用户的问题主要聚焦在如何在Python中模拟C++的std::sort行为,所以回答应围绕上述几点。 最后,生成相关问题需要考虑用户可能的后续疑问,比如性能比较、如何实现特定比较逻辑、稳定性问题,或者如何优化Python的排序实现等。</think>在Python中实现类似C++的`std::sort`功能,需结合其语言特性进行调整。以下是具体实现方法: ### 1. 原地排序 Python的列表方法`list.sort()`是原地排序,与`std::sort`的原地特性一致: ```python arr = [3, 1, 4, 1, 5, 9, 2, 6] arr.sort() # 默认升序 ``` ### 2. 自定义比较逻辑 若需复杂比较逻辑(如降序、多条件排序),可通过`functools.cmp_to_key`换函数实现[^2]: ```python from functools import cmp_to_key # 示例:奇偶数优先,数值降序 def compare(a, b): if a % 2 == b % 2: return b - a # 数值降序 else: return -1 if a % 2 == 0 else 1 arr.sort(key=cmp_to_key(compare)) ``` ### 3. 不稳定性处理 Python内置的`Timsort`是稳定排序。若需不稳定性,可手动实现快速排序: ```python def quick_sort(arr): if len(arr) <= 1: return arr pivot = arr[len(arr) // 2] left = [x for x in arr if x < pivot] middle = [x for x in arr if x == pivot] right = [x for x in arr if x > pivot] return quick_sort(left) + middle + quick_sort(right) arr = quick_sort([3, 1, 4, 1, 5, 9, 2, 6]) ``` ### 4. 性能优化 Python内置排序已高度优化,优先使用`list.sort()`或`sorted()`。若需更底层控制,可通过C扩展(如`ctypes`或`Cython`)调用C++代码,但复杂度较高[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值