看了很多排序算法,每种算法都有多个版本,现总结一版自己觉得容易理解的,供以后翻阅。
1.插入排序
直接插入排序
直接插入排序是将一个数插入到已经排序好的序列中。做法是先将第一个数作为已经排序好的,依此将后面的数取出插入到前面已排序好的序列中。
def insert_sort(nums):
for i in range(len(nums)):
for j in range(i):
if nums[j]>nums[i]:
#nums.insert(j,nums.pop(i))
nums[j],nums[i]=nums[i],nums[j]
return nums
关于插入的具体操作,这里既可以用 nums[j],nums[i]=nums[i],nums[j],也可以用 nums.insert(j,nums.pop(i))。
时间复杂度为,空间复杂度为。是稳定的排序算法。
希尔排序
先按一定的增量将数据分组,每组进行直接插入排序,随着增量的减少,每组的数越来越多,最后所有数据成为一组。
def shell_sort(nums):
step=len(nums)//2
while step>0:
for i in range(step,len(nums)):
while i>=step and nums[i-step]>nums[i]: #每组进行直接插入排序
nums[i-step],nums[i]=nums[i],nums[i-step]
i-=step
step//=2 #增量每次的变化
return nums
时间复杂度为
,空间复杂度为
。不是稳定的排序算法。
2.交换排序
冒泡排序
从序列头开始,每次比较两个元素。若他们顺序错误则进行交换,直到遍历完序列为止。
def buble_sort(nums):
for i in range(len(nums)):
for j in range(i+1,len(nums)):
if nums[i]>nums[j]:
nums[i],nums[j]=nums[j],nums[i]
return nums
时间复杂度为
,空间复杂度为
。是稳定的排序算法。
快速排序
通过一趟排序将序列分为两部分,一部分比某个数大,另一部分比某个数小。再按此方法对两部分数据进行排序,知道整个序列有序。
def quick_sort(nums):
if nums==[]:
return []
else:
partition=nums[0]
left=quick_sort([l for l in nums[1:] if l<partition])
right=quick_sort([r for r in nums[1:] if r>=partition])
return left+[partition]+right
时间复杂度为,空间复杂度为。不是稳定的排序算法。
3.选择排序
简单选择排序
第1趟,在待排序记录r1 ~ r[n]中选出最小的记录,将它与r1交换;第2趟,在待排序记录r2 ~ r[n]中选出最小的记录,将它与r2交换;以此类推,第i趟在待排序记录r[i] ~ r[n]中选出最小的记录,将它与r[i]交换,使有序序列不断增长直到全部排序完毕。
def select_sort(nums):
for i in range(len(nums)):
x=i
for j in range(i+1,len(nums)):
if nums[j]<nums[x]:
x=j
nums[x],nums[i]=nums[i],nums[x]
return nums
时间复杂度为
,空间复杂度为
。不是稳定的排序算法。
堆排序
它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]] >= A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。
def heap_sort(hlist):
def heap_adjust(parent):
child=2*parent+1 #左孩子
while child<len(heap):
if child+1<len(heap):
if heap[child]<heap[child+1]:
child+=1 #判断左右孩子里的较大值
if heap[parent]>=heap[child]:
break
heap[parent],heap[child]=heap[child],heap[parent] #最大值作为父节点
parent,child=child,child*2+1 #调整下一个
heap,hlist=hlist,[]
for i in range(len(heap)-1,-1,-1): #建立大顶堆
heap_adjust(i)
while len(heap)>0:
heap[0],heap[-1]=heap[-1],heap[0]
hlist.insert(0,heap.pop()) #弹出最大结点并进行调整
heap_adjust(0)
return hlist
时间复杂度为
,空间复杂度为
。不是稳定的排序算法。
4.归并排序
采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
def merge_sort(nums):
def merge(left,right):
array=[]
while left and right:
if left[0]<right[0]:
array.append(left.pop(0))
else:
array.append(right.pop(0))
if left:
array+=left
elif right:
array+=right
return array
def recursive(nums):
if len(nums)==1:
return nums
mid=len(nums)//2
left=recursive(nums[:mid])
right=recursive(nums[mid:])
return merge(left,right)
return recursive(nums)
时间复杂度为
,空间复杂度为
。是稳定的排序算法。
5.基数排序
对于数组中的元素,首先按照最低有效数字进行排序,然后由低位向高位进行。
对于3个元素的数组[977, 87, 960],第一轮排序首先按照个位数字相同的放在一个桶s[7]=[977],s[7]=[977,87],s[0]=[960]执行后list=[960,977,87].第二轮按照十位数,s[6]=[960],s[7]=[977],s[8]=[87],执行后list=[960,977,87].第三轮按照百位,s[9]=[960],s[9]=[960,977],s[0]=87,执行后list=[87,960,977],结束。
def radix_sort(nums):
s=[[]]
digit=0
while len(s[0])!=len(nums): #所有元素全排到第一个桶后,结束
s=[[] for i in range(10)] #定义10个桶
for i in nums:
index=i//(10**digit)%10 #从个位开始,依次入桶
s[index].append(i)
nums=[]
for i in range(len(s)):
nums+=s[i]
digit+=1
return nums
时间复杂度为O(d(r+n),空间复杂度为O(rd+n)。是稳定的排序算法。
各排序算法总结