冒泡排序,就是相邻的两个元素相互比较并根据比较结果决定是否交换位置。如从小到大排序,相邻两个元素两两比较,将值更大的元素交换到右侧,如此到最后一个元素,就能确定最大的一个值,一轮排序结束。若某一轮排序交换位置的次数为0,则排序结束。
冒泡排序基于简单交换的思想,冒泡排序每趟不断将记录两两比较,并按“前小后大”的规则交换。
冒泡排序的基本思想是:
①从数组的头部开始,比较相邻两个数,如果第1个数比第2个数大,就交换他们两个。也就是让较大的数逐渐往后移动,直到数组的末尾,经过第一轮的比较,就可以找到最大的元素,并将它移动到最后一个位置。
②第一轮结束后,继续第二轮,仍然从数组的头部开始比较,直到数组的倒数第2个元素为止,经过第2轮比较,就可以找到次大的元素,并将它放到倒数第二个位置。
③以此类推,经过n-1轮“冒泡”后,就可以将所有的元素都排列好。
冒泡排序的实例(升序):
初始: 21 25 49 25 16 08 n=6
第一趟:位置0,1进行比较--判断21<25--不交换--结果:21 25 49 25 16 08
位置1,2进行比较--判断25<49--不交换--结果:21 25 49 25 16 08
位置2,3进行比较--判断49>25--交换--结果:21 25 25 49 16 08
位置3,4进行比较--判断49>16--交换--结果:21 25 25 16 49 08
位置4,5进行比较--判断49>08--交换--结果:21 25 25 16 08 49
第一趟比较了5次,确定了最大的数字49
第二趟:位置0,1进行比较--判断21<25--不交换--结果:21 25 25 16 08 49
位置1,2进行比较--判断25=25--不交换--结果:21 25 25 16 08 49
位置2,3进行比较--判断25>16--交换--结果:21 25 16 25 08 49
位置3,4进行比较--判断25>08--交换--结果:21 25 16 08 25 49
第二趟比较了4次,确定了第二大数字25
第三趟:位置0,1进行比较--判断21<25--不交换--结果:21 25 16 08 25 49
位置1,2进行比较--判断25>16--交换--结果:21 16 25 08 25 49
位置2,3进行比较--判断25>08--交换--结果:21 16 08 25 25 49
第三趟比较了3次,确定了第三大数字25
第四趟:位置0,1进行比较--判断21>16--交换--结果:16 21 08 25 25 49
位置1,2进行比较--判断21>08--交换--结果:16 08 21 25 25 49
第四趟比较了2次,确定了第四大数字21
第五趟:位置0,1进行比较--判断21>08--交换--结果:08 16 21 25 25 49
第五趟比较了1次,确定了第五大数字16和最小的数字08
可以得出结论,n个记录的话,总共需要n-1趟,第x趟需要比较n-x次。
冒泡排序总共排的次数为:1+2+3+…+n-1,共 n(n-1)/2,时间复杂度为O(n2)。
快速排序是改进的交换排序。从本质上来看,快速排序是一种递归分治法。首先任意选取一个数据(通常选用数组的第一个数)作为枢轴,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,形成左右两个子表,这个过程称为一趟快速排序,然后对各子表重新选择中心元素并依此规则调整,直到每个子表的元素只剩下一个。通过一趟排序,将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分进行排序,以达到整个序列有序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。
快速排序的思路:
(1)从数列中挑出一个元素,称为 “基准”(pivot);
(2)将元素分而治之,小的置于基准值左侧,大的置于基准值右侧;
(3)基准值两侧作为两个子数列,再分别找一个基准值,重复步骤(1)。
快速排序的实现步骤:
两个指针left,right分别指向列表的第一个元素和最后一个元素,然后取一个参考值,默认为第一个列表的第一个元素list[0],称为K
然后left指向的值先和参考值K进行比较,若list[left]小于或等于K值,left就一直向右移动,left+1,直到移动到大于K值的地方,停住
right指向的值和参考值K进行比较,若list[right]大于K值,right就一直向左移动,right-1,直到移动到小于K值的地方,停住
此时,left和right若还没有相遇,即left还小于right,则二者指向的值互换
若已经相遇则说明,第一次排序已经完成,将list[right]与list[0]的值进行互换,进行之后的递归。
下面是用python实现的冒泡排序和快速排序:
def bubble_super(arr):
"""冒泡排序"""
loop = len(arr) - 1
flag = 1
if loop > 0:
print('初始flag=',flag)
for x in range(loop): # 总共需要 loop 趟
if flag ==1:
print('第',x+1,'趟:flag=',flag)
flag = 0
for i in range(loop - x): # 第x趟需要比较 loop-x 次
if arr[i] > arr[i + 1]: #若发生逆序
flag = 1
arr[i], arr[i + 1] = arr[i + 1], arr[i] # 则交换位置
print(' 第',i+1,'次:flag=',flag,'结果为:',arr)
else:
print(' 第',i+1,'次:flag=',flag,'结果为:',arr)
else:
print('第',x+1,'趟:flag=',flag)
return arr
num = [8,21,21,16,49,25]
print('最终结果:',bubble_super(num))
def split_array(nums, left, right): # 返回调整后基准数的位置
key = nums[left] # nums[left]就是第一个基准点
while left < right:
# right下标位置开始,向左边遍历,查找不大于基准数的元素
while left < right and nums[right] >= key:
right -= 1
if left < right: # 找到小于准基数key的元素,然后交换nums[left],nums[right]
nums[left], nums[right] = nums[right], nums[left]
else: # left〉=right 跳出循环
break
# left下标位置开始,向右边遍历,查找不小于基准数的元素
while left < right and nums[left] < key:
left += 1
if left < right: # 找到比基准数大的元素,然后交换nums[left],nums[right]
nums[right], nums[left] = nums[left], nums[right]
else: # left〉=right 跳出循环
break
nums[left] = key
print('基准点',key,'的排序结果是:',nums)
return left # 此时left==right 所以返回right也是可以的
def quick_sort(nums, left, right):
if left < right:
key_index = split_array(nums, left, right)
quick_sort(nums, left, key_index - 1)
quick_sort(nums, key_index + 1, right)
if __name__ == "__main__":
nums = [6,1,2,7,9,3,4,5,10,8]
quick_sort(nums, 0, len(nums) - 1)
print('最后的结果是:',nums)
冒泡排序的运行结果为:
快速排序的运行结果为:
结果分析
两种算法的复杂度及稳定性比较:
排序算法 | 时间复杂度(平均) | 时间复杂度(最坏) | 时间复杂度(最好) | 空间复杂度 | 稳定性 |
冒泡排序 | O(n2) | O(n2) | O(n) | O(1) | 稳定 |
快速排序 | O(nlog2n) | O(n2) | O(nlog2n) | O(log2n) | 不稳定 |
快速排序不是原地排序,需要递归调用栈的支持,而栈的长度取决于递归调用的深度(即使不用递归,也要用到用户栈)。在平均情况下,需要用到O(log2n)的栈空间,最坏情况下,栈空间可以达到O(n)。
快速排序不适合对原本有序或者基本有序的记录序列进行排序。划分元素的选取是影响时间性能的关键,输入次序越混乱,所选划分元素值的随机性就越好,排序速度就越快,快速排序不是自然排序方法。
冒泡排序是从最底层元素开始比较,(与其上的元素比较)小于就往上再比,大于就交换,再用较小的往上比较,直到最高层,第一次把最大的放到最上层,第二次把第二大的放到第二层,以次类推;快速排序是先找到一个轴值,比较时把所有比轴值小的放到轴值的左边,比轴值大的放到右边,再在两边各自选取轴值再按前面排序,直到完成。
当数据量增大时,冒泡排序所用时间如下图所示:
快速排序所用时间如下图所示:
很明显,数据量增大时,快速排序比冒泡排序效率高出很多。如果数字只有几个的话两者比较不是很明显。快速排序是所有内部排序算法中平均性能最优的排序算法。