这篇文章是我很久之前在知乎看到的,我觉得是最能把这个排序讲的这么清晰明了真不多话不多说,我暂且描述下整个排序的过程。
冒泡排序的全过程
比如给你一个列表L,如下:
[2, 4, 3, 9, 7, 5, 1, 8, 6]
首先我们将2和4比较,4比较大,就保持不变;4和3比较,4比较大,4和3交换位置如下:
[2, 3, 4, 9, 7, 5, 1, 8, 6]
然后4和9比较,保持不变;9和7比较,9大,9和7交换位置,如下:
[2, 3, 4, 7, 9, 5, 1, 8, 6]
然后9和5比较,9大,9和5交换位置,如下:
[2, 3, 4, 7, 5, 9, 1, 8, 6]
9和1比较,9大,9和1交换位置,如下:
[2, 3, 4, 7, 5, 1, 9, 8, 6]
9和8比较,9大,9和8交换位置,如下:
[2, 3, 4, 7, 5, 1, 8, 9, 6]
9和6比较,9大,9和6交换位置,如下:
[2, 3, 4, 7, 5, 1, 8, 6, 9]
至此,经过第1轮“冒泡”之后,列表中最大的数字“9”到了它该到的位置。
以此类推,那么要经过多少轮才能把这个列表中的元素全都排完呢?
如果你不知道,我们可以用一个长度为3的列表来看看:
[3, 2, 1]
① 3 > 2 => [2, 3, 1]; 3 > 1 => [2, 1, 3]; 3到此排完了
② 1 < 2 => [1, 2, 3];2到此排完了,剩下一个1,没有元素和它比较了,于是排序结束。
观察上面这个例子,3个元素列表,只需要经过2轮就能排完了,因为剩最后2个元素的时候,它们互相比较一下,这2个元素的顺序就都好了,所以当一个列表的长度为len(L)时,只需要经过len(L)-1轮,排序就结束了,于是冒泡法的第一行代码就出来了:
for i in range(len(L)-1): #只需要经过len(L)-1轮,排序即结束,i代表每一轮比较
下面再看看在每轮比较中,一共比较了多少次呢?拿刚开始的列表举例,
[2, 4, 3, 9, 7, 5, 1, 8, 6] ,经过第1轮后,这个列表变为了
[2, 3, 4, 7, 5, 1, 8, 6, 9],下一轮再比较,比到6就好了,不需要再和9比较了,对吧!所以我们发现这个规律:
第1轮,比较了len(L) - 1 = 8次;
第2轮,比较了len(L) - 2 = 7次;
第N轮,比较了len(L) - N次,于是代码又来一行:
for j in range(1, len(L)-i): #这里面的i就是当前所在的轮数,j表示每一轮要遍历的元素
#这里面range(1, len(L)-i)也可以写成range(0,len(L)-i-1),反正表示的长度是一样的,区别在于
#j的第一个值是0还是1。是0时,先得到第一个元素;是1时,先得到第二个元素。
得到遍历的元素之后,我们就要比较了,相邻的两个元素,如果前面的大于后面的,就让它们的值交换,让大的往后走,一直到这个列表中最大的元素走到列表的最后面,于是:
if L[j-1] > L[j]: # 假设当j=1时,这里是第一个元素和第二个元素比较
L[j-1], L[j] = L[j], L[j-1] # Python常用的交换变量写法
好了,我们的冒泡排序就写完了。觉得很短吗?没错,实际逻辑代码就4行。
标配代码:
L = [2, 4, 3, 9, 7, 5, 1, 8, 6]
for i in range(len(L)-1): #只需要经过len(L)-1轮,排序即结束,i代表每一轮比较
for j in range(1, len(L)-i):#这里面的i就是当前所在的轮数,j表示每一轮要遍历的元素
if L[j-1] > L[j]: # 假设当j=1时,这里是第一个元素和第二个比较
L[j-1], L[j] = L[j], L[j-1] # Python常用的交换变量写法
print(L)
还可以写成如下形式,但后续的代码都以上面的为主:
L = [2, 4, 3, 9, 7, 5, 1, 8, 6]
for i in range(len(L)-1):
for j in range(0, len(L)-i-1): #和上面一样的,只是起始索引变了
if L[j] > L[j+1]: #当j=0时,这里是第一个元素和第二个比较
L[j], L[j+1] = L[j+1], L[j]
print(L)
以上就是冒泡排序的最基础的代码。
完了?还有没有可优化的地方?面前的面试官皱着眉头对你说。你必须回答:有!
想象有这么几种特殊情况情况:
第一种:当遍历一轮发现没有元素交换时,排序即可结束。
[2, 1, 4, 3, 6, 5, 8, 7, 9] 当待排序列表为这个时,经过第1轮排序,就变成了
[1, 2, 3, 4, 5, 6, 7, 8, 9] 此时列表已经完全排好序了,但是呢,程序不知道,还在准备进入第2轮排序,前后比较,1和2比,2和3比,一直到7和8比完,一个也没交换,然后紧接着又要进入第3轮了!!停,在这块是不是可以优化下,我们发现在第2轮比较一遍后,没有任何值发生交换,那是不是可以说明这轮比较的所有元素全都是有序的了,所以才不需要交换。那我们在程序中可以添加个代表是否交换的变量,不交换时值为False,如果交换了就置为True。最后每轮结束,如果值为False就说明这轮没有发生交换,没有发生任何交换就直接结束循环,退出程序,因为这个列表已经排序好了;如果值为True就说明这轮发生交换了,需要进入下一轮,但在进入下一轮遍历前,需要先将已经置为True的变量重新设置为False。如果你想明白了,请看代码:
L = [2, 4, 3, 9, 7, 5, 1, 8, 6]
for i in range(len(L)-1):
swap = False #每一轮都要重置一次swap的值,所以要写在这边
for j in range(1, len(L)-i):
if L[j-1] > L[j]:
swap = True #每次交换就改变swap的值
L[j-1], L[j] = L[j], L[j-1]
if not swap: #当swap为False时,退出循环
break
print(L)
因为我找不到原链接地址,如果涉及侵权,立即删除