Este artículo se divide principalmente en tres partes:
-
Problemas básicos de partición
-
Problema de la bandera holandesa
-
Clasificación rápida clásica y clasificación rápida aleatoria
1. Problema de partición básica
Dado un arreglo arr y un número num, coloque el número menor o igual a num a la izquierda de la matriz, y el número mayor que num a la derecha de la matriz, lo que requiere complejidad de espacio adicional O (1), complejidad de tiempo O (N )
# 1. 快排基础 partition思想
# 给定一个数组arr,和一个数num,请把小于等于num的数放在数组的左边,大于num的数放在数组的右边
# 要求额外空间复杂度O(1),时间复杂度O(N)
import numpy as np
arr = list(np.random.randint(10, size=6))
num = int(np.random.randint(10,size=1))
def partition(arr, num):
"""划分数组, 时间复杂度O(n),空间复杂度O(1)"""
idx_left = 0
idx_right = len(arr)
while idx_left < idx_right : # 终止条件要考虑清楚
if arr[idx_left] <= num:
idx_left += 1
print('left', arr, idx_left, idx_right)
else:
idx_right -= 1
arr[idx_left], arr[idx_right] = arr[idx_right], arr[idx_left]
print('right', arr, idx_left, idx_right)
return arr
print(arr)
print(num)
res = partition(arr, num)
print('res', res)
2. La bandera holandesa
# 2. 荷兰国旗问题
# 给定一个数组arr,和一个数num,请把小于num的数放在数组的左边,等于num的数放在数组的中间,大于num的数放在数组的右边。
# 要求额外空间复杂度O(1),时间复杂度O(N)
def helan_flag(arr, num):
"""划分数组, 时间复杂度O(n),空间复杂度O(1)"""
# 设立三个辅助index
idx_left = -1
idx_right = len(arr)
idx_current = 0
while idx_current < idx_right: #注意终止条件
if arr[idx_current] < num:
idx_left += 1
arr[idx_left], arr[idx_current] = arr[idx_current], arr[idx_left]
idx_current += 1
elif arr[idx_current] > num:
idx_right -= 1
arr[idx_current], arr[idx_right] = arr[idx_right], arr[idx_current]
else:
idx_current += 1
return arr
# test
res = helan_flag([3,5,5,5,7,8,9,1,2,3,5,5,5], 5)
print('res', res)
3. Fila rápida clásica y fila rápida aleatoria
# 3. 快排
# 3.1 经典快排 将arr最后一位数x作为划分值,分成2块,小于等于x的放左边,大于x的放右边
# 3.2 改进快排 将arr最后一位数作为划分值, 结合荷兰国旗问题,分成3块, 小于x放左边,等于x放中间,大于x放右边
# why 改进后的快排比经典快排更好? (经典快排一次只搞定了一个位置的数,即x,改进的快排一次搞定了所有等于x的数)
def partition(arr, left, right):
if len(arr) <= 1:
return arr
num = arr[-1]
idx_left = left - 1
idx_right = right
idx_current = left
while idx_current < idx_right:
if arr[idx_current] < num:
idx_left += 1
arr[idx_left], arr[idx_current] = arr[idx_current], arr[idx_left]
idx_current += 1
elif arr[idx_current] > num:
idx_right -= 1
arr[idx_right], arr[idx_current] = arr[idx_current], arr[idx_right]
else:
idx_current += 1
# 把最后一位数划分值 放到中间
arr[-1], arr[idx_right] = arr[idx_right], arr[-1]
idx_right += 1
return idx_left, idx_right
def quick_sort(arr):
if len(arr) <= 1:
return arr
left, right = partition(arr, 0, len(arr)-1)
mid = arr[left+1:right]
return quick_sort(arr[:left+1]) + mid + quick_sort(arr[right:])
import numpy as np
flag = True
for i in range(1000):
arr = list(np.random.randint(100, size=50))
res1 = quick_sort(arr)
arr.sort()
if arr != res1:
flag = False
print('res1', res1)
print('arr', arr)
break
print(flag)
# 3.3 随机快排 时间复杂度O( N*logN )
# 改进快排还可以进一步改进为随机快排,总是固定拿arr最后一位数当作划分值可能造成划分的子问题不是等规模的,与待排序的数据状况有关
# 例如,原数组是升序排列的,[1, 2, 3, 4, 5, 6, 7], 每次partition只搞定一个数,快排退化成O(n^2)
# 想绕开数据原始的数据状况一般有两种方式: 随机选取 & 哈希
import numpy as np
def partition(arr, left, right):
if len(arr) <= 1:
return arr
##与普通快排的区别之处,随机选择一个位置的数和arr最后一个位置的数进行交换
idx_random = int(np.random.randint(len(arr), size=1))
arr[idx_random], arr[-1] = arr[-1], arr[idx_random]
############################################## 接下来和3.2一样
num = arr[-1]
idx_left = left - 1
idx_right = right
idx_current = left
while idx_current < idx_right:
if arr[idx_current] < num:
idx_left += 1
arr[idx_left], arr[idx_current] = arr[idx_current], arr[idx_left]
idx_current += 1
elif arr[idx_current] > num:
idx_right -= 1
arr[idx_right], arr[idx_current] = arr[idx_current], arr[idx_right]
else:
idx_current += 1
# 把最后一位数划分值 放到中间
arr[-1], arr[idx_right] = arr[idx_right], arr[-1]
idx_right += 1
return idx_left, idx_right
def quick_sort(arr):
if len(arr) <= 1:
return arr
left, right = partition(arr, 0, len(arr)-1)
mid = arr[left+1:right]
return quick_sort(arr[:left+1]) + mid + quick_sort(arr[right:])
# test
flag = True
for i in range(1000):
arr = list(np.random.randint(100, size=50))
res1 = quick_sort(arr)
arr.sort()
if arr != res1:
flag = False
print('res1', res1)
print('arr', arr)
break
print(flag)