排序问题形式化定义:
输入:由n个数构成的一个序列{a1,a2,…,an}
输出:对输入序列的一个排列{a1’,a2’,…,an’},使得a1’<=a2’<=…<=an’
在计算机科学中,排序是一种基本操作,它是很多程序中会调用到的一个方法。不同的排序算法在不同条件下拥有各自的优劣,掌握各种排序算法的特点,不仅有助于我们编写特定程序时的选用,而且可以帮助我们理解算法复杂度的计算。
如何洗牌,即打乱一个序列,Knuth洗牌算法可以做到遍历一遍数组便将其完全打乱,即时间复杂度O(n)。
import random
# random、numpy.random模块都有shuffle方法,这里顺便自己实现一下
class Shuffle:
@staticmethod
def knuthShuffle(a):
for i in range(0, len(a)): # 从左到右遍历数组
r=random.randint(0,i) # 生成一个0-i之间的随机数
a[i], a[r] = a[r], a[i] # 交换
这里的排序基类定义了各种排序算法需要用到的操作:比较大小、交换等,这样可以使排序算法的代码更加易读。
class Sort:
def sort(self, a):
pass
@staticmethod
def less(small, big):
return small < big
@staticmethod
def exch(a, i, j):
a[i], a[j] = a[j], a[i]
def isSorted(self, a):
for i in range(1,len(a)):
if self.less(a[i], a[i-1]):
return False
return True
依次对每一个元素a[i],找到右边所有元素中最小的一个min{a[i+1],…,a[N-1]},与该元素交换。
class Selection(Sort):
def sort(self, a):
# 依次对每一个元素a[i]
for i in range(0,len(a)):
# 找到右边所有元素中最小的一个min{a[i + 1], ..., a[N - 1]}
mini = i
for j in range(i+1,len(a)):
if self.less(a[j],a[mini]):
mini = j
#与该元素交换
self.exch(a,i,mini)
从左到右依次将每一个元素a[i],插入到左边有序数组{a[0],…,a[i-1]}中合适的位置,使a[0]…a[i]依然有序。
遍历+交换: 时间复杂度O(N^2)。
最好情况:对有序数组O(N),N-1次比较,0次交换;
最坏情况:逆序数组O(N^2),N*(N-1)/2次比较,N*(N-1)/2次交换
class Insertion(Sort):
def sort(self, a):
for i in range(1,len(a)):
for j in range(i,0,-1):
if self.less(a[j],a[j-1]):
self.exch(a,j,j-1)
else:
break
冒泡排序与插入排序类似,都是相邻两元素作交换。
class Bubble(Sort):
def sort(self, a):
sortedflag=True
for i in range(0,len(a)):
for j in range(i,len(a)): #冒泡排序每一趟都会将最小的元素换到最前面
if self.less(a[j],a[i]):
self.exch(a,j,i)
sortedflag=False
if sortedflag: # 若第一趟无元素交换,说明数组有序,直接return
print("already sorted!")
return
希尔排序是一种基于插入排序的快速的排序算法,其思想是使数组中任意间隔h的元素都是有序的,目的是为了减少元素的移动距离
class Shell(Sort):
def sort(self, a):
h=1
while h3:
h=3*h+1
while h>=1:
for i in range(h,len(a)):
for j in range(i,h-1,-h):
if self.less(a[j],a[j-h]):
self.exch(a,j,j-h)
h=int(h/3)
经过多次测试,四种初级排序算法用时平均为10:12:15:9。
冒泡排序时间最长,Shell最短但与选择排序不相上下。
通过运行时间我们也可以大致看出,数组个数由1000增至10000,用时增至原来的100倍,由此也可以推出以上初级排序算法的时间复杂度为O(n^2)。
import time
l = [i for i in range(10000)] # 生成一个0-9999的列表
Shuffle.knuthShuffle(l) # 洗牌
print(l)
l1,l2,l3,l4=list(l),list(l),list(l),list(l)
selesort=Selection()
start=time.time()
selesort.sort(l1)
end=time.time()
print("Selection sort running time:",end-start,l1)
insersort = Insertion()
start = time.time()
insersort.sort(l2)
end = time.time()
print("Insertion sort running time:", end - start, l2)
bubsort = Bubble()
start = time.time()
bubsort.sort(l3)
end = time.time()
print("Bubble sort running time:", end - start, l3)
shellsort = Shell()
start = time.time()
selesort.sort(l4)
end = time.time()
print("Shell sort running time:", end - start, l4)
(参考资料:《算法(第四版)》)