注:本文如涉及到代码,均经过Python 3.7实际运行检验,保证其严谨性。
本文阅读时间约为8分钟。
今天要介绍的是两种排序及其算法分析:冒泡排序和选择排序。
冒泡排序(Babble Sort)
冒泡排序的算法思路在于对无序表进行多趟比较交换。每趟包括了多次两两相邻比较,并将逆序的数据项互换位置,最终能将本趟的最大项就位。
就这样,经过n-1趟比较交换,实现整表排序。
每趟的过程类似于每两个相邻气泡比赛上浮,胜利的那个“气泡”在水中上浮,这一幕不断上演的经过。这就是其被命名为“冒泡排序”的原因所在。
冒泡排序是我们能想到的最直观的排序方法。
下面看看冒泡排序的具体过程:
第1趟比较交换,共有n-1对相邻数据进行比较。最大项在这n-1次比较中脱颖而出。
第2趟比较交换,由于最大项已经脱离了剩余项的比较层次,需要排序的数据项由n项减少为n-1项。于是我们共有n-2对相邻数据进行比较。
不断重复这个过程,直到第n-1趟完成后,最小项一定在列表的首位,就无需再处理了。
根据这个思路,写出来的冒泡排序的参考代码如下:
# 冒泡排序算法。
def bubbleSort(alist):
for passnum in range(len(alist) - 1, 0, -1): # n-1趟比较。
for i in range(passnum):
# 每一项和前面一项作比较,如果它比前面的一项大,就会两两交换位置。
if alist[i] > alist[i + 1]:
alist[i], alist[i + 1] = alist[i + 1], alist[i]
alist = [54, 26, 93, 17, 77, 31, 44, 55, 20]
bubbleSort(alist)
print(alist)
<<<[17, 20, 26, 31, 44, 54, 55, 77, 93]
冒泡排序的算法分析
冒泡排序总需要n-1趟,随着趟数的增加,比对次数逐步从n-1减少到1,并包括可能发生的数据项交换。
总的比对次数是第1趟到第(n-1)趟的次数累加,即:。
而比对的时间复杂度为。
关于交换次数,时间复杂度也是,通常每次交换包括3次赋值。
最好的情况是,列表在排序前已经是有序的,交换的次数为0;最差的情况是,每次比对都要进行交换,交换的次数等于比对次数;平均情况则为最差情况的一半。
冒泡排序是对人来说,最直观的排序算法,一般作为其它算法的对比基准。
冒泡排序的一大缺点就是,时间效率非常差,这是因为,每个数据项在找到其最终位置之前,必须经过多次比对和交换,而一般在这个过程中,大部分的操作都是无效的。
尽管如此,冒泡排序并非一无是处,它至少有两点优势:
第一,冒泡排序只需要在原来的列表当中操作即可,无需任何额外的存储空间开销。
第二,其适用范围较广。由于冒泡排序只涉及到两两相邻的数据项之间的比对和交换,所以,它不仅适用于列表这种顺序存储的数据结构,还能用于别的算法鞭长莫及的链式等数据结构。
冒泡排序的性能改进
在原有基础上,我们可以通过监测每趟比对是否发生过交换,可以提前确定排序是否完成。换句话说,如果某趟比对没有发生任何交换,说明列表已经排好序,后面的比对不用再继续进行下去了,可以提前结束算法,从而减少比对次数。
改善后的代码如下:
# 冒泡排序算法的改进版。
def bubbleSort(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
alist[i], alist[i + 1] = alist[i + 1], alist[i]
passnum = passnum - 1
alist = [54, 26, 93, 17, 77, 31, 44, 55, 20]
bubbleSort(alist)
print(alist)
<<<[17, 20, 26, 31, 44, 54, 55, 77, 93]
尽管这种改善相比之前的算法能提前结束,但并不能改变冒泡算法整体的算法复杂度。
在这个思路上诞生了另外一种排序算法:选择排序(Select Sort)。
选择排序(Select Sort)
选择排序在冒泡排序的基础上进行了改进,保留了其基本的多趟比对思路,每趟都使当前最大项就位。
但是,选择排序对冒泡排序的交换次数进行了消减。相比冒泡排序进行多次交换,选择排序每趟仅进行1次交换,记录最大项的所在位置,最后再跟本趟最后一项交换。
所以,选择排序算法的时间复杂度比冒泡排序稍稍强一点:其比对的复杂度和冒泡排序一样,都是,而其交换的复杂度却是,明显优于冒泡排序的。
基于上述思路,选择排序的算法代码如下:
# 选择排序的算法。
def selectionSort(alist):
for fillslot in range(len(alist) - 1, 0, -1):
positionOfMax = 0
for location in range(1, fillslot + 1):
# 每一项和前面一项作比较,如果它比前面的一项大,仅仅是记录位置,而不是两两交换位置。
if alist[location] > alist[positionOfMax]:
positionOfMax = location
alist[fillslot], alist[positionOfMax] = alist[positionOfMax], alist[fillslot]
To be continued.