递归
递归
分治
merge sort
对原list不断二分,最小序列为两个元素或一个元素的序列(二分时间复杂度O(logn),对最小序列排序(两元素比较大小)
然后子序列两两归并(合并两个升序数组,O(n)),总时间复杂度为O(nlogn),空间复杂度:需要新列表储存所有元素,O(n)
由于两个子序列合并时,对于值相等的元素,总是先复制左边再复制右边,不会改变原序列元素的位置,因此归并排序是稳定排序算法
"merge sort"
class MergeSort:
#########合并两个升序lsit#########
def merge(self,l1,l2):
l=[]
while l1 and l2:
a,b=l1[0],l2[0]
if a<=b:
l.append(a)
del l1[0]
else:
l.append(b)
del l2[0]
if l1==[]:return l+l2
if l2==[]:return l+l1
def mergesort(self,l):
####停止条件,子序列只含有1个或2个元素####
if len(l)==1:return l
if len(l)==2:
#####对两个元素排序####
if l[0]<=l[1]:return l
else:
l[0],l[1]=l[1],l[0]
return l
#####二分######
left,right=l[:len(l)//2],l[len(l)//2:]
#####归并######
return self.merge(self.mergesort(left),self.mergesort(right))
quick sort
思想:选择一个基准值,比基准值小的在左侧,比基准值大的在右边
对左右两侧的子序列分别选择新的基准值再次划分,至子序列只剩下一个或两个元素
复杂度:
最差情况:快排性能取决于基准值的选择,如果输入是一个有序数列,每次基准值取序列的第一个元素,那么每次左侧的子序列为空,需要取n次基准值才能得到最小序列。每次取子序列,需要比较所有元素与基准值的大小关系,时间复杂度O(n),总时间复杂度O(n^2)
最好情况:对于一个有序序列,每次基准值都取中间的元素,即每次对原序列不断二分,需要取logn次,因此最好时间复杂度为O(nlogn)
平均情况:每次随机选择一个元素作为基准值,其性能将逼近O(nlogn)
空间复杂度O(logn)
快排会改变相同值原来的相对位置,不稳定
"quick sort"
import random
def QuickSort(L):
###终止条件:子序列只有1或0个元素####
if len(L)<2:return L
#######随机取一个元素为基准值########
p=random.randint(0,len(L)-2)
pivot=L[p]
#####将小于或等于基准值的元素放到左序列,大于基准值的放到右序列#######
left=[i for i in L[:p] if i<=pivot]+[i for i in L[p+1:] if i<=pivot]#注意排除掉作为基准值的元素
right=[i for i in L[:p] if i>pivot]+[i for i in L[p+1:] if i>pivot]
return QuickSort(left)+[pivot]+QuickSort(right)
快排与归并排序性能比较
快排随机取基准值,其平均性能接近最优性能,时间复杂度为O(nlogn)
虽然平均时间复杂度都为O(nlogn),但是快排完成一次操作的单位时间远快于归并排序(归并需要合并升序序列),因此平均情况快排的速度要快很多。