冒泡排序:Python语言实现

145 阅读4分钟

冒泡排序:Python语言实现

冒泡排序多次遍历列表。它比较相邻的元素,将不合顺序的交换。每一轮遍历都将下一个最 大值放到正确的位置上。本质上,每个元素通过“冒泡”找到自己所属的位置。

下图展示了冒泡排序的第一轮遍历过程。深色的是正在比较的元素。如果列表中有 n 个元素,那么第一轮遍历要比较 n–1 对。注意,最大的元素会一直往前挪,直到遍历过程结束。

bubble_sort.jpg

第二轮遍历开始时,最大值已经在正确位置上了。还剩 n–1 个元素需要排列,也就是说要比较 n–2 对。既然每一轮都将下一个最大的元素放到正确位置上,那么需要遍历的轮数就是 n–1。 完成 n–1 轮后,最小的元素必然在正确位置上,因此不必再做处理。

下面给出了完整的bubble_sort 函数。该函数以一个列表为参数,必要时会交换其中的元素。

def bubble_sort(alist):
    for passnum in range(len(alist)-1, 0, -1):
        for i in range(passnum):
            if alist[i] > alist[i+1]:
                temp = alist[i]
                alist[i] = alist[i+1]
                alist[i+1] = temp

测试代码:

a_list = [54, 26, 93, 17, 77, 31, 44, 55, 20]
print(f"before sorting: {a_list}")
bubble_sort(a_list)
print(f"after sorting: {a_list}")
assert a_list == sorted(a_list)

输出:

before sorting: [54, 26, 93, 17, 77, 31, 44, 55, 20]
after sorting: [17, 20, 26, 31, 44, 54, 55, 77, 93]

性能测试:

from random import randint, seed
seed(13)
lst_to_sort = [randint(100, 999) for _ in range(1000)]
%timeit bubble_sort(lst_to_sort)

结果:

17.6 ms ± 190 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)

在分析冒泡排序算法时要注意,不管一开始元素是如何排列的,给含有 n 个元素的列表排序总需要遍历 n–1 轮。下表展示了每一轮的比较次数。总的比较次数是前 n–1 个整数之和。由于 前 n 个整数之和是 12n2+12n\frac{1}{2}n^2+\frac{1}{2}n 因此前 n–1 个整数之和就是 12n2+12nn\frac{1}{2}n^2+\frac{1}{2}n-n12n212n\frac{1}{2}n^2-\frac{1}{2}n。这表明, 该算法的时间复杂度是 O(n2)O(n^2) 。在最好情况下,列表已经是有序的,不需要执行交换操作。在最 坏情况下,每一次比较都将导致一次交换。

轮次比较次数
1n-1
2n-2
3n-3
......
n-11

冒泡排序通常被认为是效率最低的排序算法,因为在确定最终的位置前必须交换元素。“多余”的交换操作代价很大。不过,由于冒泡排序要遍历列表中未排序的部分,因此它具有其他排 序算法没有的用途。特别是,如果在一轮遍历中没有发生元素交换,就可以确定列表已经有序。可以修改冒泡排序函数,使其在遇到这种情况时提前终止。对于只需要遍历几次的列表,冒泡排 序可能有优势,因为它能判断出有序列表并终止排序过程。

下面实现了如上所述的修改,这种排序通常被称作短冒泡

def short_bubble_sort(alist):
    exchanges = True
    passnum = len(alist)-1
    while passnum > 0 and exchanges:
        exchanges = False
        for i in range(passnum):
            if alist[i] > alist[i+1]:
                exchanges = True
                temp = alist[i]
                alist[i] = alist[i+1]
                alist[i+1] = temp
        passnum = passnum - 1

测试代码:

a_list = [54, 26, 93, 17, 77, 31, 44, 55, 20]
print(f"before sorting: {a_list}")
short_bubble_sort(a_list)
print(f"after sorting: {a_list}")
assert a_list == sorted(a_list)

输出:

before sorting: [54, 26, 93, 17, 77, 31, 44, 55, 20]
after sorting: [17, 20, 26, 31, 44, 54, 55, 77, 93]

性能测试:

from random import randint, seed
seed(13)
lst_to_sort = [randint(100, 999) for _ in range(1000)]
%timeit short_bubble_sort(lst_to_sort)

结果:

37.6 μs ± 581 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)

参考文档

《Python数据结构与算法分析(第2版)》:5.3.1 冒泡排序