冒泡排序原理
1.比较相邻的元素。如果第一个比第二个大,就交换他们两个;否则,位置不变。
2.对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。最后的元素应该会是最大的数。
3.针对所有的元素重复以上的步骤,除了最后一个。
4.持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
python实现
根据冒泡排序的定义,我们可以用python代码实现
def bubble_sort(items):
for i in range(len(items) - 1): #i变量用来控制排序循环的次数(比如 n个数,需要进行 n-1次冒泡排序)
for j in range(len(items) - 1 - i): #j变量控制冒泡过程,即相邻两元素比较,每次排完一次序,最后一个元素可以不参与下一次排序,所以要减去i
if items[j] > items[j + 1]:
items[j], items[j + 1] = items[j + 1], items[j]
i += 1 #统计排序次数
print('共循环%d次'% i)
return items
arr = [1,2,8,4,7,5,6]
bubble_sort(arr)
运行结果如下:
共循环6次
[1, 2, 4, 5, 6, 7, 8]
可以看到,arr数组共有7个元素,于是进行了6次冒泡排序,成功得出排序数组。这是最普通的冒泡排序的算法实现,应该不难理解,但是这种写法有一个缺点: 假设现在有一个数组[6,1,2,3,4,5],当我们进行完第一次冒泡排序过程后变为[1,2,3,4,5,6],这时候,数组已经变成有序的了,程序要是再继续循环就比较浪费资源。为了解决这个问题,就引申出一些优化方法:
冒泡排序优化方法一:
上述数组[6,1,2,3,4,5]在第一次排序后已经是一个有序数组[1,2,3,4,5,6],当再次进行排序时,会发现任意两个相邻元素之间的位置都不会再变动,意味着已经排好序了。利用这个特性,我们可以在程序中添加一面“旗帜”,这面“旗帜”告诉计算机,现在相邻元素都不再变动位置了,证明已经排好序啦!,可以结束程序了!所以,我们重新指定一个新的变量,令他初始值为False,当发生位置变动时,我们令它为True,当元素不再变换位置时,我们立刻让该变量变成初始值False,退出循环程序,这样,就可以减少排序循环的次数啦!
看下优化后的冒泡排序算法代码:
def bubble_sort1(items):
for i in range(len(items) - 1):
flag = False #设置一面‘旗帜’,用来控制程序什么时候结束
for j in range(len(items) - 1 - i):
if items[j] > items[j + 1]:
items[j], items[j + 1] = items[j + 1], items[j]
flag = True #发生排序的动作,则赋值为True,证明未排序成功,继续循环;否则,证明排序完成,进入下一个 ‘if not flag’语句
i += 1
if not flag: #排序成功,则退出循环
break
print('共循环%d次'% i)
return items
arr = [1,2,8,4,7,5,6]
bubble_sort1(arr)
运行结果如下:
共循环3次
[1, 2, 4, 5, 6, 7, 8]
从运行结果可以看到,循环次数由原来6次变成3次,效率提升一倍哦。
冒泡排序优化方法二:
上面的写法虽然效率有所提升,但是还有一个问题,就是每次都是从左边到右边进行比较,这样效率不高,如果能 双向排序,即当一次从左向右的排序比较结束后,立马从右向左来一次排序比较。这样效率一定会更高!这其实就引申出了另外一种,基于冒泡排序改进的排序方法:搅拌排序 / 鸡尾酒排序。
看下再次优化后的代码:
def bubble_sort2(items):
for i in range(len(items) - 1):
flag = False
for j in range(len(items) - 1 - i):
if items[j] > items[j + 1]:
items[j], items[j + 1] = items[j + 1], items[j]
flag = True
if flag: #反向排序
flag = False
for j in range(len(items) - 2 - i, 0, -1):
if items[j - 1] > items[j]:
items[j], items[j - 1] = items[j - 1], items[j]
flag = True
i += 1
if not flag:
break
print('共循环%d次'% i)
return items
arr = [1,2,8,4,7,5,6]
bubble_sort2(arr)
看下运行结果:
共循环2次
[1, 2, 4, 5, 6, 7, 8]
果然,运行效率又得到提升了!循环次数由之前的3次变成了2次。
时间复杂度
1.若文件的初始状态是正序的,一趟扫描即可完成排序。所以,冒泡排序最好的时间复杂度为 O(n) 。
2.若初始文件是反序的,需要进行 n-1 趟排序。每趟排序要进行 n-i 次关键字的比较(1≤i≤n-1),且每次比较都必须移动记录三次来达到交换记录位置。在这种情况下,冒泡排序的最坏时间复杂度为 O(n2) 。
算法稳定性
冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。所以,如果两个元素相等,是不会再交换的;如果两个相等的元素没有相邻,那么即使通过前面的两两交换把两个相邻起来,这时候也不会交换,所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。