目录
总览
十大排序从入门到入赘 | yukiyama (iyukiyama.github.io)
https://iyukiyama.github.io/sorting/十大排序算法超全大综合,动图演示,你真的值得拥有! - 力扣(LeetCode)
https://leetcode.cn/circle/article/rzsN73/
比较类排序
交换排序
冒泡排序
左右比较,使最值冒出来 稳定
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
# 冒泡排序
n = len(nums)
for i in range(n-1,0,-1):
for j in range(0,i):
if nums[j+1]<nums[j]:
nums[j+1],nums[j] = nums[j],nums[j+1]
return nums
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
# 冒泡排序
n = len(nums)
end = n-1 # 记录有序区下界
for i in range(n-1,0,-1):
is_sweap = False # 记录本轮次是否有交换 可以提前跳出循环
for j in range(end):
if nums[j+1]<nums[j]:
nums[j+1],nums[j] = nums[j],nums[j+1]
is_sweap = True
end = j
if not is_sweap:
break
return nums
快速排序
选定基准,以基准分区 不稳定 # 7 2 4 4 8 9
递归
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
# 快速排序
n = len(nums)
def quick(left,right):# 左闭右闭
if right<=0 or left>n or left>=right:return
mid = left+(right-left)//2
pivot = nums[mid]
nums[mid],nums[left] = nums[left],nums[mid]
slow = left
for fast in range(left+1,right+1):
if nums[fast]<=pivot:
slow+=1
nums[slow],nums[fast] = nums[fast],nums[slow]
nums[left],nums[slow] = nums[slow],nums[left]
quick(left,slow-1)
quick(slow+1,right)
quick(0,n-1)
return nums
栈
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
# 快速排序
n = len(nums)
stack = []
stack.append([0,n-1])
while stack:
e = stack.pop()
left = e[0]
right = e[1]
if right<=0 or left>n or left>=right:
continue
mid = left+(right-left)//2
pivot = nums[mid]
nums[mid],nums[left] = nums[left],nums[mid]
slow = left
for fast in range(left+1,right+1):
if nums[fast]<=pivot:
slow+=1
nums[slow],nums[fast] = nums[fast],nums[slow]
nums[left],nums[slow] = nums[slow],nums[left]
stack.append([slow+1,right])
stack.append([left,slow-1])
return nums
插入排序
简单插入排序
把无序数插入到有序区的对应位置 稳定
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
# 插入排序
n = len(nums)
for i in range(1,n):# 指向要进行操作的数
tmp = nums[i]
index = i-1
while index>=0 and nums[index]>tmp: # 插入
nums[index+1] = nums[index]
index-=1
nums[index+1]=tmp
return nums
希尔排序
本质上来说,希尔排序也是插入排序,或者说简单插入排序是gap = 1的希尔排序
不稳定 # {0, 1, 4, 3, 3, 5, 6}
希尔排序的时间复杂度与增量序列的选择有关。最优复杂度增量序列尚未明确。
对于增量,还是用一个数组存储吧 倒推变换太复杂了
常用增量
Shell 增量
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
# 插入排序
n = len(nums)
gap = n//2 # 步长
while gap: # 直至步长为0
for i in range(gap): # 分区
index = i+gap # 要插入的节点
while index<n:
tmp = nums[index]
j = index
while j-gap>=0 and tmp<nums[j-gap]: # 插入排序
nums[j] = nums[j-gap]
j -=gap
nums[j] = tmp # 插入的位置
index+=gap # 处理下一次节点
gap//=2
return nums
Hibbard增量
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
# 插入排序
n = len(nums)
gap = 1 # 步长
while gap<=n:
gap *=2
gap = gap//2-1
while gap: # 直至步长为0
for i in range(gap): # 分区
index = i+gap # 要插入的节点
while index<n:
tmp = nums[index]
j = index
while j-gap>=0 and tmp<nums[j-gap]: # 插入排序
nums[j] = nums[j-gap]
j -=gap
nums[j] = tmp # 插入的位置
index+=gap # 处理下一次节点
gap =(gap+1)//2-1
return nums
Knuth增量
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
# 插入排序
n = len(nums)
gap = 1 # 步长
while gap<=2*n+1:
gap *=3
gap = (gap//3-1)//2
while gap: # 直至步长为0
print(gap)
for i in range(gap): # 分区
index = i+gap # 要插入的节点
while index<n:
tmp = nums[index]
j = index
while j-gap>=0 and tmp<nums[j-gap]: # 插入排序
nums[j] = nums[j-gap]
j -=gap
nums[j] = tmp # 插入的位置
index+=gap # 处理下一次节点
gap =((gap*2+1)//3-1)//2
return nums
Sedgewick增量
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
# 插入排序
n = len(nums)
gap = 1 # 步长
gaps = []
k =1
while gap<n:
gaps.append(gap)
gap = 4**k+3*2*(k-1)+1
k+=1
while gaps: # 直至步长为0
gap = gaps.pop()
for i in range(gap): # 分区
index = i+gap # 要插入的节点
while index<n:
tmp = nums[index]
j = index
while j-gap>=0 and tmp<nums[j-gap]: # 插入排序
nums[j] = nums[j-gap]
j -=gap
nums[j] = tmp # 插入的位置
index+=gap # 处理下一次节点
return nums
选择排序
简单选择排序
一轮遍历选最值 置于有序区边界 不稳定 # 7 7 2
import math
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
# 选择排序
n = len(nums)
for i in range(n-1,-1,-1):
tmp = -math.inf
index = -1
for j in range(0,i+1):
if nums[j]>tmp:
tmp = nums[j]
index = j
nums[i],nums[index] = nums[index],nums[i]
return nums
堆排序
基于数据结构堆(优先队列) 不稳定
建堆:所有非叶子节点逆序下沉
排序:堆顶与有序区下届交换 下沉调整堆
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
# 堆排序
n = len(nums)
def down(father,n):# 下沉
if n<=0:return
left = 2*father+1
right = left+1
while left<n:
child = left
if right<n and nums[father]<nums[right] and nums[left]<nums[right]:
child = right
if nums[father]>nums[child]:
break
nums[father],nums[child] = nums[child],nums[father]
father = child
left = 2*father+1
right = left+1
index = (n-1)//2
while index>=0:# 建堆
down(index,n)
index-=1
for i in range(n-1,0,-1):# 堆排序
nums[0],nums[i] = nums[i],nums[0]
down(0,i)
return nums
归并排序
分治思想 ,将原待排数组 递归或迭代 地分为左右两半,直到数组长度为 1,然后合并左右数组,在合并中完成排序。
自顶向下:从输入数组出发,不断二分该数组,直到数组长度为1,再执行合并。递归
自底向上 :从输入数组的单个元素出发,一一合并,二二合并,四四合并直到数组有序。迭代
是否原地:是否借助辅助空间
稳定
二路归并排序
自顶向下 非原地
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
# 归并排序 自顶向下 递归
def combine(a,b): # 左闭右闭
if b==a:return
combine(a,(b-a)//2+a)
combine((b-a)//2+a+1,b)
nums1 = nums[a:(b-a)//2+a+1]
nums2 = nums[(b-a)//2+a+1:b+1]
n = len(nums1)
m = len(nums2)
while m or n:
if m and n:
if nums1[n-1]>nums2[m-1]:
nums[a+n+m-1] = nums1[n-1]
n-=1
else:
nums[a+n+m-1] = nums2[m-1]
m-=1
elif m:
nums[a:a+m] =nums2[0:m]
m = 0
else:
n = 0
return
n = len(nums)
combine(0,n-1)
return nums
自底向上 非原地
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
# 归并排序 自底向上 迭代
gap =1
l = len(nums)
while gap<l:
for j in range(0,l,2*gap):
left = j
right = min(j+2*gap-1,l-1)
nums1 = nums[left:left+gap]
nums2 = nums[left+gap:right+1]
n = len(nums1)
m = len(nums2)
while n or m:
if n and m and nums1[n-1]>nums2[m-1]:
nums[left+m+n-1] = nums1[n-1]
n-=1
elif n and m:
nums[left+m+n-1] = nums2[m-1]
m-=1
elif m:
nums[left:left+m] = nums2[0:m]
m = 0
else:
n = 0
gap*=2
return nums
自顶向下 原地
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
# 归并排序 自顶向下 递归
def combine(a,b): # 左闭右闭
if b==a:return
combine(a,(b-a)//2+a)
combine((b-a)//2+a+1,b)
left = a # 记数组一起始位置
right = (b-a)//2+a+1# 数组二起始位置
while left<right and right<=b:
while left<=b and nums[left]<=nums[right]: # 找到左边数组第一个大于右边数组首元素的值
left+=1
index = right# 记下right位置
while right<=b and left<=b and nums[right]<nums[left]:# 找到右边数组第一个大于等于left元素的值
right+=1
# 旋转三次
re(left,index-1)
re(index,right-1)
re(left,right-1)
# 移动left
left+=right-index
return
def re(left,right):
while left<right:
nums[left],nums[right] = nums[right],nums[left]
left+=1
right-=1
n = len(nums)
combine(0,n-1)
return nums
自底向上 原地
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
def re(left,right):
while left<right:
nums[left],nums[right] = nums[right],nums[left]
left+=1
right-=1
# 归并排序 自底向上 迭代
gap =1
l = len(nums)
while gap<l:
for j in range(0,l,2*gap):
left = j # 记数组一起始位置
b = min(j+2*gap-1,l-1) # 数组二终止位置
right = left+gap # 数组二起始位置
while left<right and right<=b:
while left<=b and nums[left]<=nums[right]: # 找到左边数组第一个大于右边数组首元素的值
left+=1
index = right# 记下right位置
while right<=b and left<=b and nums[right]<nums[left]:# 找到右边数组第一个大于等于left元素的值
right+=1
# 旋转三次
re(left,index-1)
re(index,right-1)
re(left,right-1)
# 移动left
left+=right-index
gap*=2
return nums
多路归并
非比较类排序
计数排序
核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
不稳定 优化后可稳定
优化前不稳定
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
# 计数排序
n = len(nums)
end = max(nums)
start = min(nums)
tmp = [0]*(end-start+1)
res = []
for num in nums:
tmp[num-start]+=1
for i in range(end-start+1):
while tmp[i]:
res.append(i+start)
tmp[i]-=1
return res
优化后稳定
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
# 计数排序
n = len(nums)
end = max(nums)
start = min(nums)
tmp = [0]*(end-start+1)
res = [0]*n
for num in nums:
tmp[num-start]+=1
for i in range(1,end-start+1):# j对应到它的下标
tmp[i]+=tmp[i-1]
for i in range(n-1,-1,-1):# 倒序
res[tmp[nums[i]-start]-1] = nums[i]
tmp[nums[i]-start]-=1
return res
桶排序
计数排序的升级版,利用了函数的映射关系:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排)。
高效:
- 在额外空间充足的情况下,尽量增大桶的数量;
- 使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中。
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
# 桶排序
n = len(nums)
buckets = [[]for _ in range(n)]
min_num = min(nums)
max_num = max(nums)
if min_num==max_num:return nums
for num in nums:
index = (num-min_num)*(n-1)//(max_num-min_num)
buckets[index].append(num)
res = []
for bucket in buckets:
bucket.sort()
res +=bucket
return res
基数排序
将整数按位数切割成不同的数字,然后按每个位数分别比较。
要求正数 需要预处理 但有可能越界 因此可以将[0,9]扩展到[-9,9]
获得最大值的位数 按每一位对进行基数排序
基数排序要求对每一位的排序 (计数排序或非计数排序) 必须是稳定排序,否则无法得到正确结果。
以计数排序为基础
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
# 基于计数排序的基数排序
n = len(nums)
buckets = [[]for _ in range(n)]
max_num = max(max(nums),-min(nums)) # 绝对值最大
width = 0
while max_num:
max_num//=10
width+=1
k = 0
while k<width:
buckets = [0]*20
for num in nums:
if num>=0:
index = num//10**k%10+10
buckets[index]+=1
else:
num = -num
index = num//10**k%10
buckets[9-index]+=1
for i in range(1,20):
buckets[i]+=buckets[i-1]
tmp = [0]*n
for i in range(n-1,-1,-1):
num = nums[i]
if num>=0:
index = num//10**k%10+10
tmp[buckets[index]-1] = num
buckets[index]-=1
else:
num = -num
index = num//10**k%10
tmp[buckets[9-index]-1] = -num
buckets[9-index]-=1
nums = tmp
k+=1
return nums
以非计数排序(如桶排序)为基础
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
# 基于桶排序的基数排序
n = len(nums)
buckets = [[]for _ in range(n)]
max_num = max(max(nums),-min(nums)) # 绝对值最大
width = 0
while max_num:
max_num//=10
width+=1
k = 0
while k<width:
buckets = [[] for _ in range(20)]
for num in nums:
if num>=0:
index = num//10**k%10+10
buckets[index].append(num)
else:
num = -num
index = num//10**k%10
buckets[9-index].append(-num)
nums = []
for bucket in buckets:
nums+=bucket
k+=1
return nums