冒泡排序是最基础的排序算法
什么是冒泡排序??
冒泡排序是一种基础的交换排序。
冒泡、冒泡就像水中的气泡一样,不断的往上漂浮,而冒泡算法之所以叫冒泡算法,正是因为这种算法的每一个元素都可以像小气泡一样,一点一点的向数组的一端移动。
冒泡排序的详细过程
我们把相邻的元素两两比较,当一个元素大于右侧相邻元素时,交换它们的位置;当一个元素小于或等于右侧相邻的元素时,则位置不变。 详细过程如下:
不断的交换蓝色的数字
5 | 8 | 6 | 3 | 9 | 2 | 1 | 7 |
---|---|---|---|---|---|---|---|
5 | 6 | 8 | 3 | 9 | 2 | 1 | 7 |
5 | 6 | 3 | 8 | 9 | 2 | 1 | 7 |
5 | 6 | 3 | 8 | 2 | 9 | 1 | 7 |
5 | 6 | 3 | 8 | 2 | 1 | 9 | 7 |
5 | 6 | 3 | 8 | 2 | 1 | 7 | 9 |
从最左端开始,数组中最大的元素9像汽水一起,漂浮到最右端
这时,冒泡排序的第一轮就完成了,有序区域目前就只有一个元素。
5 | 6 | 3 | 8 | 2 | 1 | 7 | 9 |
---|
下面,进行第二轮排序:
5 | 6 | 3 | 8 | 2 | 1 | 7 | 9 |
---|---|---|---|---|---|---|---|
5 | 3 | 6 | 8 | 2 | 1 | 7 | 9 |
5 | 3 | 6 | 2 | 8 | 1 | 7 | 9 |
5 | 3 | 6 | 2 | 1 | 8 | 7 | 9 |
5 | 3 | 6 | 2 | 1 | 7 | 8 | 9 |
第2轮结束后,数列右侧的有序区有2个元素,顺序如下:
5 | 3 | 6 | 2 | 1 | 7 | 8 | 9 |
---|
这样不断的迭代,直到所有的元素都是有序的为止,这就是冒泡排序的整体思想。
冒泡排序是一种稳定的排序,值相等的元素并不会打乱原本的顺序。由于该排序算法每一轮都要遍历所有元素,总共遍历(元素数量-1)轮,所有平均时间复杂度是O(n^2)
代码实现(第一版)
def sort(array):
for i in range(len(array) - 1): # 外循环控制所有回合
for j in range(len(array) -i -1): # 内循环实现每一轮的冒泡处理
if array[j] > array[j+1]: # 如果左大于右,则交换
array[j],array[j+1] = array[j+1],array[j]
return array
验证
输入:
array = [5,8,6,3,9,2,1,7]
sort(array)
输出:
[1, 2, 3, 5, 6, 7, 8, 9]
优化(一)
有时,数组中的元素已经排好序了,但是程序还是会继续执行,一直到遍历完所有的轮数(元素个数-1)
在这种情况下,如果可以提前判断数组中元素已经排好序,并标记出来,剩下的轮数就可以不用执行了!
代码实现(第二版)
def sort(array):
for i in range(len(array) -1): # 外循环控制所有回合
issorted = True # 判断是否排序好了,初始为true
for j in range(len(array) -i-1): # 内循环实现每一轮的冒泡处理
if array[j] > array[j+1]: # 如果左大于右,则交换
array[j],array[j+1] = array[j+1],array[j]
issorted = False # 如果有交换,说明没有排好序,则置issorted为False
if issorted : # 如果issorted为True说明已经交换完成了,则直接退出循环
break
return array
验证
输入:
array = [5,8,6,3,9,2,1,7]
sort(array)
输出:
[1, 2, 3, 5, 6, 7, 8, 9]
相比第一版,第二版做了一些小的改动,利用布尔变量issorted 作为标记。如果在本轮排序中,元素交换,则说明数组无序;如果没有元素交换,则说明数组已经有序,则直接跳出大循环。
优化(二)
用一个新的数组来举例说明:
3 | 4 | 2 | 1 | 5 | 6 | 7 | 8 |
---|
我们发现上面数组的左半部分是无序的,右半部分是有序,我们继续采用上面的代码,会出现明明右半部分已经排好序了,但是每一轮中,数组中相邻的元素还是会不断的去比较,故这块可以优化一下。
代码实现(第三版)
def sort(array):
for i in range(len(array) -1): # 外循环控制所有回合
issorted = True # 判断是否排序好了,初始为true
sortBorder = len(array) -1 # 无序数组的边界,每一次更新到这里就结束了
for j in range(sortBorder): # 内循环实现每一轮的冒泡处理
if array[j] > array[j+1]: # 如果左大于右,则交换
array[j],array[j+1] = array[j+1],array[j]
issorted = False # 如果有交换,说明没有排好序,则置issorted为False
sortBorder = j # 将无序数组的边界更新为最后一次交换元素的位置
if issorted : # 如果issorted为True说明已经交换了,则直接退出循环
break
return array
验证
输入:
array =[3,4,2,1,5,6,7,8]
sort(array)
输出:
[1, 2, 3, 5, 6, 7, 8]
优化二的关键问题在于数列有序区的界定。
按照现在的逻辑,有序区的长度和排序的轮数是一样的,即第一轮排序过后,有序区长度是1,第二轮排序后,有序区长度是2,等等
但实际上,有序区的长度可能要比认定的长度长很多,如上面的例子一样。
那么我们该如何解决呢? 我们可以在每一轮交换完之后,记录一下最后一次元素交换的位置,这个位置就是无序数组和有序数组的界限。