冒泡排序法
def bubbleSort(ls):
for j in range(len(ls)-1,0,-1):
for i in range(j):
if ls[i]>ls[i+1]:
ls[i],ls[i+1]=ls[i+1],ls[i]
对比的时间复杂度是O(n2),交换的复杂度也是O(n2)
冒泡排序法通常作为时间效率较差的排序法,作为其他算法的对比基准,其效率差在每个数据项在找到其最终位置前必须经过多次无效的交换。
优势是无需任何额外的存储空间开销,在源列表的空间内进行。
冒泡排序法优化
def bubbleSortSmart(ls):
exchange=True
n=len(ls)-1
while n>0 and exchange:
exchange=False
for i in range(n):
if ls[i]>ls[i+1]:
exchange=True
ls[i],ls[i+1]=ls[i+1],ls[i]
n-=1
优化版添加了变量exchange,如果某次一系列对比都没发生任何交换,证明列表已经有序,结束循环。
选择排序法
def selectionSort(ls):
for i in range(len(ls)-1,0,-1):
max=0
for j in range(1,i+1):
if ls[j]>ls[max]:
max=j
ls[i],ls[max]=ls[max],ls[i]
保留了冒泡排序法多趟对比的思路,但是对交换进行了优化,每次仅进行一次交换,记录最大项的索引,最后再跟本次最后一项交换
对比次数复杂度是O(n2),但是交换的复杂度是O(n)
归并排序
归并排序:使用的是递归的思想,先不断二分然后排列,最后返回并组合成完整的排序列表。
算法
def merge_sort(ls):
if len(ls)<=1:
return ls
#分裂
middle=len(ls)//2
#分别对左右半列表进行排序
left=merge_sort(ls[:middle])
right=merge_sort(ls[middle:])
#归并
merge=[]#缓存表
while left and right:#两个表都不为空,就会持续循环
if left[0]<=right[0]:#对两个排序表的最小项进行比较
merge.append(left.pop(0))#将小的那个弹出来,添加到缓存表
else:
merge.append(right.pop(0))
#有一个表空了就会退出循环,但此时l或r表中可能会有剩余。
merge.extend(right if right else left)
return merge
分析
分裂过程借鉴二分查找的分析结果,是对数复杂度,为O(log n)
归并过程,每个元素都要进行一次比较和放置,所以是线性复杂度O(n)
总的时间复杂度应该是O(nlog n)
归并算法使用了额外一倍的存储空间,典型的用空间换时间。
快速排序
算法
#分裂函数:功能是选取一个‘对比值’,所有大于对比值的后移,小于对比值的前移
def patition(ls,first,last):
mid=ls[first]#选取‘中值’,方便起见选取列表第一位
leftmark=first+1
rightmark=last#左右标初值
done=True
while done:
while leftmark<=rightmark and ls[leftmark]<=mid:#向右移动左标
leftmark+=1
while ls[rightmark]>=mid and leftmark<=rightmark:#向左移动右标
rightmark-=1
if leftmark>rightmark:#两标交叉,结束循环
done=False
else:
ls[leftmark],ls[rightmark]=ls[rightmark],ls[leftmark]#左右标值互换
ls[first],ls[rightmark]=ls[rightmark],ls[first]#'中值'就位
return rightmark
def quickSort(ls):
quickSortHelper(ls,0,len(ls)-1)
def quickSortHelper(ls,first,last):
if first<last:
splitpoint=patition(ls,first,last)
quickSortHelper(ls,first,splitpoint-1)
quickSortHelper(ls,splitpoint+1,last)
分析
时间复杂度分析:快速排序分为两部分,分裂和移动。如果每次分裂都能分成均匀两部分,那么复杂度就是O(log n)
移动需要将每一项与中值进行对比,复杂度是O(n)
综合起来就是O(nlog n),且不需要额外储存空间
缺点是中值选取过于偏离中部,造成左右分裂不平衡,复杂度可能到O(n2),加上递归的开销,比冒泡排序法还糟糕
所以问题的关键就是选择“中值”
插入排序
def insertionSort(ls):
for index in range(1,len(ls)):
cache=ls[index]#每一次要排序的元素复制到缓存里
cacheid=index#缓存的索引值
while cacheid>0 and ls[cacheid-1]>cache:
ls[cacheid]=ls[cacheid-1]
cacheid-=1
ls[cacheid]=cache
谢尔排序
插入排序的优化
def shellSort(ls):
subls=len(ls)//2#间隔设置
while subls>0:
for startposition in range(subls):
gapInsertionSort(ls,startposition,subls)#对子序列进行排序
print('After increments of size',subls,'The list is',ls)
subls//=2#缩小间隔
def gapInsertionSort(ls,start,gap):#子序列排序函数,主体是插入排序
for i in range(start+gap,len(ls),gap):
current=ls[i]
position=i
while position>=gap and ls[position-gap]>current:
ls[position]=ls[position-gap]
position-=gap
ls[position]=current