排序可以分为:
- 内部排序:数据记录在内存中进行排序
- 外部排序:因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。
内部排序又可以分为:
比较排序,时间复杂度O(nlogn) ~ O(n^2),主要有:冒泡排序,选择排序,插入排序,归并排序,快速排序,希尔排序,堆排序等。
非比较排序,时间复杂度可以达到O(n),主要有:计数排序,基数排序,桶排序等
下面依次介绍每种排序算法
1冒泡排序bubble sort
基本思想:通过两两比较,每次确定一个最大的元素
def bubble_sort(nums):
for i in range(len(nums)-1):
for j in range(len(nums)-i-1):
if nums[j]>nums[j+1]:
nums[j+1],nums[j]=nums[j],nums[j+1]
return nums
2选择排序select sort
基本思想:数组前段有序,后段无序;每次将后段中的最小元素选择出来,将其与后段的第一个元素交换位置
def select_sort(nums):
for i in range(len(nums)):
index=nums.index(min(nums[i:]))
nums[i],nums[index]=nums[index],nums[i]
return nums
3插入排序 insert sort
基本思想:数组前段有序,后段无序;每次将后段中的第一个元素插入到前段中合适的位置def insert_sort(nums):
for i in range(1,len(nums)):
temp=nums[i]
j=i-1
while(j>=0)and nums[j]>temp :
nums[j+1]=nums[j]
j-=1
nums[j+1]=temp
return nums
4归并排序 merge sort
基本思想:
将原数组分成左右两个子序列,假如每个子序列是按升序排列的,依次比较它们的第一个元素,可以将其合并成一个更大的升序序列;用递归的方法处理子序列,直到子序列的长度为1或2,可以很容易地对其进行排序
def merge_sort(nums):
if len(nums) <2:
return nums
mid = int((len(nums) / 2))
left = merge_sort(nums[:mid])
right = merge_sort(nums[mid:len(nums)])
result = []
while len(left) > 0 and len(right) > 0:
if (left[0] <= right[0]):
result.append(left.pop(0))
else:
result.append(right.pop(0))
if (len(left) > 0):
result.extend(left)
else:
result.extend(right)
return result
5快速排序 quick sort
基本思想:
将数组的第一个元素作为基准pivot,将剩余元素按照是否大于该基准分为两组:less和greater;如果这两组元素都是有序的,则最终的顺序为less+pivot+greater;递归地处理两个子分组,直到它们变成有序的为止
def quick_sort(nums):
if len(nums) < 2:
return nums
else:
pivot = nums[0]
# sub-array of all the elements less than the pivot
less = [i for i in nums[1:] if i <= pivot]
# sub-array of all the elements greater than the pivot
greater = [i for i in nums[1:] if i > pivot]
return quick_sort(less) + [pivot] + quick_sort(greater)
6希尔排序shell sort
基本思想:缩减增量排序
先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高。
def shell_sort(nums):
gap=int(len(nums)/2) #排序的分组
while gap>0:
for i in range(gap,len(nums)):
j=i-gap
while nums[j]>nums[i] and j >=0:
nums[j],nums[i]=nums[i],nums[j]
j-=gap
i-=gap
gap=int(gap/2)
return nums
7堆排序 heap sort
基本思想:
堆排序是利用堆的特性对记录序列进行排序的一种排序方法。堆可以看做一个完全二叉树,同时该完全二叉树满足双亲结点大于等于孩子结点(大顶堆),或者双亲结点小于等于孩子结点(小顶堆)。 大顶堆产生顺序序列,小顶堆产生逆序序列。
N个元素建立二叉堆,将原来无序的序列(数组)插入到二叉树(线性二叉树),在插入的过程中不断调整二叉树为最小堆或者最大堆。 在全部插入完毕之后,此时并未排序完毕,但是已经建立起最小堆或者最大堆,堆中每一个根节点都是当前树或子树中的最小值或最大值,此时还需要进行进一步的调整来完成最终的排序。
def heap_sort(nums):
def sift_down(nums, start, end):
root = start
while True:
# 从root开始对最大堆调整
child = 2 * root + 1
if child > end:
break
# 找出两个child中较大的一个
if child + 1 <= end and nums[child] <nums[child + 1]:
child += 1
if nums[root] < nums[child]:
# 最大堆小于较大的child, 交换顺序
nums[root], nums[child] = nums[child], nums[root]
# 正在调整的节点设置为root
root = child
else:
# 无需调整的时候, 退出
break
# 从最后一个有子节点的节点开始调整最大堆
first = len(nums) // 2 - 1
for start in range(first, -1, -1):
sift_down(nums, start, len(nums) - 1)
# 将最大的放到堆的最后一个, 堆-1, 继续调整排序
for end in range(len(nums) -1, 0, -1):
nums[0], nums[end] = nums[end], nums[0]
sift_down(nums, 0, end - 1)
return nums
8桶排序
基本思想:
假设输入数据的范围确定,可以按照一定的跨度将该区间划分为若干个桶。把每一个输入数据放到相应的桶里,同时对每一个桶内部进行排序。最后再把桶合并。
def bucket_sort(nums):
#设置桶的大小
buckets = [0] * ((max(nums) - min(nums))+1)
#设置桶中元素的值
for i in range(len(nums)):
buckets[nums[i]-min(nums)] += 1
#将桶中元素存到B,此时B有序
B=[]
for i in range(len(buckets)):
if buckets[i] != 0:
B += [i+min(nums)]*buckets[i]
return B
9计数排序
基本思想:
利用哈希的方法,将每个数据出现的次数都统计下来。哈希表是顺序的,所以我们统计完后直接遍历哈希表,将数据再重写回原数据空间就可以完成排序。
def count_sort(nums):
#最终排好序的数组
B=[0]*len(nums)
#计算用来存储计数的数组C的长度
maxNum=max(nums)
minNum=min(nums)
cLength=maxNum-minNum+1
C=[0]*cLength
#将原数组中数字出现的次数存储到C中
for i in range(len(nums)):
#datalist[i]-min表示datalist中下标为i的值应该放到C中哪个位置去
C[nums[i]-minNum]+=1
#将C中数组元素的值(每个值出现的次数)加上前一个数字出现的次数
for i in range(1,cLength):
C[i]+=C[i-1]
#遍历A中元素,将它放入B中最终应该在的位置
for i in range(len(nums)):
#C[datalist[i]-minNum]表示截止datalist[i],小于等于datalist[i]的有多少
B[C[nums[i]-minNum]-1]=nums[i]
#c中记录的值得数量应该减1,因为那个对应的元素已经到B里面了
C[nums[i]-minNum]-=1
return B
10基数排序
基本思想:
按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。
def radix_sort(nums):
bucket = [[], [], [], [], [], [], [], [], [], []] #can not use [[]]*10
bucket1 = [[], [], [], [], [], [], [], [], [], []]
for i in nums:
bucket[i%10].append(i)
lst1 = []
for b in bucket:
lst1.extend(b)
for i in lst1:
index=int(i/10%10)
bucket1[index].append(i)
lst2 = []
for b in bucket1:
lst2.extend(b)
return lst2
一些参考链接:
排序可视化 https://visualgo.net/en/sorting
排序算法复杂度速查表 https://linux.cn/article-7480-1.html
常见排序算法的分析及实现
https://blog.youkuaiyun.com/qq_33414271/article/details/78528891
https://blog.youkuaiyun.com/qq_33414271/article/details/79129353