活动地址:CSDN21天学习挑战赛
冒泡排序是一种最基础的交换排序。之所以叫做冒泡排序,因为每一个元素都可以像小气泡一样,根据自身大小一点一点向数组的一侧移动。
每一趟只能确定将一个数归位。即第一趟只能确定将末位上的数归位,第二趟只能将倒数第 2 位上的数归位,依次类推下去。如果有 n 个数进行排序,只需将 n-1 个数归位,也就是要进行 n-1 趟操作。
而 “每一趟 ” 都需要从第一位开始进行相邻的两个数的比较,将较大的数放后面,比较完毕之后向后挪一位继续比较下面两个相邻的两个数大小关系,重复此步骤,直到最后一个还没归位的数。
def bubble_sort(l):
for i in range(len(lst)): # 外循环的趟数
for j in range(len(lst) - 1):
if l[j] > l[j + 1]:
l[j],l[j+1]=l[j+1],l[j]
print(l)
lst = [4, 2, 8, 0, 5, 7, 1, 3, 6, 9]
bubble_sort(lst)
其实我们可以看到上面的代码有可以优化的地方,每一趟外循环的时候,内循环比较包括的交换的次数是相等的,但是其实这是完全没必要的,因为每执行一次外循环之后,都会有一个极值通过冒泡变成了有序的,所以我们这个时候只需要找一下规律,看一下每一次内循环的次数应该是多少,应该是:
元素数 - 外循环的当前值 - 1
这个时候上面的元素可以优化为:
# 优化版一
def bubble_sort(l):
for i in range(len(lst)): # 外循环的趟数
for j in range(len(lst) - i - 1):
if l[j] > l[j + 1]:
l[j], l[j + 1] = l[j + 1], l[j]
print(l)
lst = [4, 2, 8, 0, 5, 7, 1, 3, 6, 9]
bubble_sort(lst)
但其实这个算法还是有可以优化的地方,比如我们在执行了外循环的中的几趟,这个时候元素已经就全局有序了,并不需要再进行后续外循环的执行了,这个时候我们可以认为在一趟的外循环中如果一次交换都没有,那么我们就可以认为这个列表全局有序了,这个时候就可以跳出外循环,我们使用一个标记进行记录内循环中是否有元素进行交换,可以得到下面的代码:
# 优化版二
def bubble_sort(l):
for i in range(len(lst)): # 外循环的趟数
is_sorted = True
for j in range(len(lst) - i - 1):
if l[j] > l[j + 1]:
is_sorted = False # 有元素交换,所以不是有序,标记变为False
l[j], l[j + 1] = l[j + 1], l[j]
if is_sorted: # 一趟下来是否发生位置交换,如果没有交换直接跳出外循环
break
print(l)
lst = [4, 2, 8, 0, 5, 7, 1, 3, 6, 9]
bubble_sort(lst)
还有一个问题。那如果数列中前半部分是无序的,后半部分是有序的呢?比如(3,4,2,1,5,6,7,8)这个数组,其实后面的许多元素已经是有序的了,但是每一轮还是白白比较了许多次呢?
如果按冒泡排序代码原始版来分析的话,有序区的长度和排序的轮数是相等的。比如第一轮排序过后的有序区长度是1,第二轮排序过后的有序区长度是2 ……。
但是呢,实际数列真正的有序区可能会大于这个长度,也就是你上面这个例子,第二轮中后面 5 个实际上都已经属于有序区了。因此后面的比较是没有意义的了。
我们可以这样做来避免这种情况:在每一轮排序的最后,记录一下最后一次元素交换的位置,那个位置也就是无序数列的边界,再往后就是有序区了。
# 优化版三
def bubble_sort(l):
for i in range(len(lst)): # 外循环的趟数
is_sorted = True
# 无序数列的边界,每次比较只需要比到这里为止
unsorted_border = (len(lst) - i - 1) if i == 0 else unsorted_border
for j in range(unsorted_border):
if l[j] > l[j + 1]:
print(l)
is_sorted = False # 有元素交换,所以不是有序,标记变为False
l[j], l[j + 1] = l[j + 1], l[j]
lastIndex = j
unsorted_border = lastIndex
if is_sorted:
break
lst = [3, 4, 2, 1, 5, 6, 7, 8]
bubble_sort(lst)
由上图可知,4 个石子的时候排完序需要 3 趟,第一趟需要比较3次,第二趟需要比较2次,第三趟需要比较1次,那一共比较了 3 + 2 + 1 次;
那如果有 n 个石子呢?