排序算法
为了测试各个算法的复杂度,直接用以下程序生成10000个数字的列表进行排序:
import random
lis = [random.randint(1,1000) for ii in range(10000)]
首先测试内置排序算法sorted()函数的运行时间,sorted()函数使用的是归并排序,后面也会复现。
import time
start=time.time()
base_sort=sorted(lis)
end=time.time()
interval=end-start
print(interval)
## 0.0016279220581054688
冒泡排序
冒泡排序实际上就是第i个元素不断的与ii+1的元素两两比较,将最大值往后移,例如:
[6, 9, 10, 5, 7, 7, 8, 8]
[6, 9, 5, 10, 7, 7, 8, 8]
[6, 9, 5, 7, 10, 7, 8, 8]
[6, 9, 5, 7, 7, 10, 8, 8]
[6, 9, 5, 7, 7, 8, 10, 8]
[6, 9, 5, 7, 7, 8, 8, 10]
[6, 5, 9, 7, 7, 8, 8, 10]
[6, 5, 7, 9, 7, 8, 8, 10]
[6, 5, 7, 7, 9, 8, 8, 10]
[6, 5, 7, 7, 8, 9, 8, 10]
[6, 5, 7, 7, 8, 8, 9, 10]
[5, 6, 7, 7, 8, 8, 9, 10]
实现代码如下:
## 冒泡排序
def bubble_sort(lis):
switch=True
while switch:
cc=0
for ii in range(len(lis)-1):
if lis[ii]>lis[ii+1]:
cc+=1
lis[ii],lis[ii+1]=lis[ii+1],lis[ii]
if cc==0:
switch=False
return lis
从代码就可以看出,最优时间复杂度:O(n) (表示遍历一次发现没有任何可以交换的元素,排序结束。)最坏时间复杂度:O(n2)(while的地方循环次数也为列表的的长度)。稳定性:稳定
10000个随机数字测试:
start=time.time()
sort_def_bubble=bubble_sort(lis)
end=time.time()
interval=end-start
print(interval)
## 13.968239068984985
选择排序
先从所有的序列中找到最小的,放在第一位,再从除了第一个位置的序列中找到最小的放在第二位置,再从除了第一位置和第二位置的序列中找到最小的放在第三个位置,以此类推。
代码实现:
def sel_sort(lis):
for ii in range(len(lis)-1):
min_pos=ii # 先假设最小值在第ii位置,后面发现比他更小的就替换掉
for jj in range(ii,len(lis)):
if lis[jj]<lis[min_pos]:
min_pos=jj
if min_pos!=ii:
lis[ii],lis[min_pos]=lis[min_pos],lis[ii]
return lis
最优时间复杂度:O(n2);最坏时间复杂度:O(n2);稳定性:不稳定
测试:
start=time.time()
sort_def_sel=sel_sort(lis)
end=time.time()
interval=end-start
print(interval)
## 3.3619589805603027
插入排序
如果第二个位置的数小于第一个位置的数,则第二个数与第一个数交换,第三个数如果小于第一和第二个数,则第三个数依次与第二个数,第一个数交换直到挪到第一个位置,以此类推。例如:
1,5,3,2,6
1,3,5,2,6
1,3,2,5,6
1,2,3,5,6
代码实现:
def inset_sort(lis):
for ii in range(1,len(lis)):
for jj in range(ii,0,-1):
if lis[jj]<lis[jj-1]:
lis[jj-1],lis[jj]=lis[jj],lis[jj-1]
return lis
最优时间复杂度:O(n) (升序排列,序列已经处于升序状态);最坏时间复杂度:O(n2);稳定性:稳定
测试:
start=time.time()
sort_def_sel=insert_sort(lis)
end=time.time()
interval=end-start
print(interval)
## 4.615377902984619
希尔排序(插入排序衍生)
插入算法的升级版本,每次按照一定的步长把原始列表变成一个数组,对数组的列进行排序,在进一步缩小步长,直到步长为1,即为传统的插入排序。
代码实现:
def shell_sort(lis):
gap=len(lis)//2
while gap>=1:
for ii in range(len(lis)):
jj=ii
while jj>=gap and lis[jj]<lis[jj-gap]:
lis[jj],lis[jj-gap]=lis[jj-gap],lis[jj]
jj-=gap
gap=gap//2
最优时间复杂度:根据步长序列的不同而不同;最坏时间复杂度:O(n2);稳定性:不稳定
测试:
start=time.time()
sort_def_sel=shell_sort(lis)
end=time.time()
interval=end-start
print(interval)
## 0.026904821395874023
快速排序(分而治之策略,递归算法)
随便选一个baseline,比如列表中的第一个数,找到比第一个数小的数们放在第一个数左边,找到比第一个数大的数们,放在第一个数右边,然后再对【比第一个数小的数们】和【比第一个数大的数们】形成的列表分别进行快速排序。直到列表被拆分成只含有一个元素的列表,则返回列表本身。
代码如下:
def quick_sort(lis):
if len(lis) < 2:
return lis
baseline=lis[0]
big_lis=[ii for ii in lis[1:] if ii>baseline]
small_lis=[ii for ii in lis[1:] if ii<=baseline]
return quick_sort(small_lis)+[baseline]+quick_sort(big_lis)
最优时间复杂度:O(nlogn),最坏时间复杂度:O(n2);稳定性:不稳定
测试:
start=time.time()
sort_def_sel=quick_sort(lis)
end=time.time()
interval=end-start
print(interval)
## 0.0341188907623291
归并排序(分而治之策略,递归算法)
将数组从中间的位置分成两组,分别进行归并排序得到两个有序序列A和B,然后A和B分别取第一个数作比较,比如A[0]<B[0],A[0]放进新数组,B[0]继续与A[1]比较,直到所有的数都写进新数组。核心思想还是递归,比较难写的部分是如何合并?代码如下
def merge_sort(lis):
if len(lis)<2:
return lis
middle_pos=len(lis)//2
head_lis=merge_sort(lis[:middle_pos])
tail_lis=merge_sort(lis[middle_pos:])
# 合并head_lis和tail_lis
ii=0 # 标记head_lis各个元素位置
jj=0 # 标记tail_lis各个元素位置
merge_lis=[] # 写进一个新列表
while ii<len(head_lis) and jj<len(tail_lis):
if head_lis[ii]<tail_lis[jj]:
merge_lis.append(head_lis[ii])
ii+=1
else:
merge_lis.append(tail_lis[jj])
jj+=1
# head_lis和tail_lis一定会有一个优先遍历完,留下还没有遍历的
if ii != len(head_lis):
merge_lis.extend(head_lis[ii:])
else: # jj != len(tail_lis)
merge_lis.extend(tail_lis[jj:])
return merge_lis
最优时间复杂度:O(nlogn);最坏时间复杂度:O(nlogn);稳定性:稳定
测试:
start=time.time()
sort_def_sel=merge_sort(lis)
end=time.time()
interval=end-start
print(interval)
## 0.04941534996032715