-
交换排序
交换排序的核心思想是通过交换位置来对序列进行排序(其实很多排序都会用到交换操作,但它们的思想,或者说思考角度并不是交换,而只是将交换作为实现的一种手段。)
冒泡排序基本上是大学学习编程写的第一个“程序”,姑且看作算法的启蒙,而快速排序是工业界最受欢迎的排序算法之一,可以看做龙头老大。这“一头一尾”可谓将算法设计中思想的重要性体现的淋漓尽致。
那么同样是基于交换思想的算法,为什么冒泡排序的平均时间复杂度是 ,而快速排序却是 呢?接下来让我们一探究竟。
-
冒泡排序
· 最优时间复杂度:
· 最坏时间复杂度:
· 平均时间复杂度:
· 空间复杂度:
· 稳定性:稳定排序基本思想是:遍历数组,对 相邻 的两个数进行比较,较大的数项表尾方向移动,而较小的数向表头方向移动。
如果将顺序表的表头看作温泉的池底,表位看作温泉的页面,那么就相当于每次小的元素往水底移动,大的元素向页面冒出。正所谓“冒泡”…有点牵强…“Talking is cheap,show me the code!”,直接看 Python 代码吧。
def bubble(array): n = len(array) # 从前向后两两比较,第 i 轮会将第 i 大的元素放到倒数第 i 个位置 for i in range(n): # 因为第 i 轮会确定第 i 大的元素,那么倒数 i 个元素已经有序 # 所以后面的比较可以截止到倒数第 i 个元素为止 # 相当于前 n-i 个元素没有经过排序,而后 i 个元素经过排序 for j in range(n-i-1): # 比较相邻的两个元素 if array[j] > array[j+1]: # 如果第二个数小,则交换位置 array[j], array[j+1] = array[j+1], array[j] return array
接下来对冒泡排序耗时进行计算
from random import randint import time input_data = [randint(0, 20000) for _ in range(10000)] start = time.clock() bubble(input_data) end = time.clock() # 此次排序耗费时间为: 8.10140057378438 # 通过多次运行后耗时基本在 8(+/-0.2)s print(end - start)
还有一个冒泡排序的改进版,在前 n-i 元素有序的情况下不做多余的比较:
def bubble_improve(array): n = len(array) for i in range(n): # flag = 0 代表没有发生过交换 flag = 0 for j in range(n-i-1): if array[j] > array[j+1]: array[j], array[j+1] = array[j+1], array[j] # 如果发生交换那么 flag 就不为 0 flag += 1 # 内层循环遍历的是未排序列,如果遍历未排序列没有发生交换, # 则证明没有排序的序列已经有序,那么可以直接退出 # 这样的改进可以减少对已经有序的比较次数 if not flag: return return array
对此改进版本的耗时进行计算:
from random import randint import time input_data = [randint(0, 20000) for _ in range(10000)] start = time.clock() bubble_improve(input_data) end = time.clock() # 此次排序耗费时间为: 8.810628180255106 # 通过多次运行后耗时基本在 8.8(+/-0.4)s print(end - start)
说是改进,实际上增加了很多次判断,而判断操作也是有一定耗时的,所以“改进版”也不一定好~
-
快速排序
· 最优时间复杂度:
· 最坏时间复杂度:
· 平均时间复杂度:
· 空间复杂度:
· 稳定性:不稳定排序冒泡排序只能与相邻的元素进行比较,那么可不可以与非相邻元素比较呢?答案就是快速排序。
基本思想:找元素在顺序表中的正确位置,对一个顺序表使用两个指针,分别指向表头与表尾,选取表头的元素作为被比较元素,移动两个指针进行比较,两个指针重合位置即为正确位置。一趟排序后确定被比较元素的正确位置,即此元素的左面所有元素都比他小,右面所有元素都比他大。而后将两边的元素作为两个顺序表再使用此方法递归实现。
看 Python 代码:
def sort(array, start, end): # 判断此数组是否只有一个元素 if start >= end: return # start,end 记录表头表尾位置,low,high 进行移动 low, high = start, end # 选出被比较元素 obj = array[end] # 判断两指针是否重合(实际上是 high 在 low 的左面一个位置) while low < high: # 移动 low 指针,如果小于被比较元素则继续移动 while low < high and array[low] <= obj: low += 1 # 如果大于被比较元素则将这个大的元素赋值给此时 high 所指元素 #(high 此时指示的位置的元素已经是重复元素,之前已经被换位) else: array[high] = array[low] # 移动 high指针,如果大于被比较元素则继续移动 while low < high and array[high] > obj: high -= 1 # 如果小于被比较元素则将这个小的元素赋值给此时 low 所指元素 else: array[low] = array[high] # 将被比较元素赋值到正确位置 array[low] = obj # 根据表头与表尾位置以及此时被比较元素的正确位置拆分顺序表, # 递归进行以上相同的操作 sort(array, start, low-1) sort(array, low+1, end)
接下来对快速排序耗时进行计算:
from random import randint import time input_data = [randint(0, 20000) for _ in range(10000)] print(input_data) start = time.clock() sort(input_data, 0, len(input_data)-1) print(input_data) end = time.clock() # 此次排序耗费时间为: 0.02883855227136317 # 通过多次运行后耗时基本在 0.02(+/-0.01)s print(end - start)
可以看到快速排序的排序速度比冒泡排序快非常多,在 n=10000 时近乎 300 倍!这就是算法、思想的力量!
数据结构算法 - 交换排序(冒泡、冒泡改进 and 快速排序)
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_37352167/article/details/84982888
猜你喜欢
转载自blog.csdn.net/weixin_37352167/article/details/84982888
今日推荐
周排行