https://github.com/lzneu/Algrithm_python
O(n*logn)級別的排序:
|-歸併排序
分成log(n)個層級 每個層級進行O(n)排序
每次歸併時 開闢一個新的存儲空間作爲輔助 因此需要使用O(n)多的空間
用三個索引進行歸併 時間複雜度O(n)
當n比較小的時候 由於複雜度前面都有常數項 因此 歸併有時比插入排序滿 可在此處進行優化
自底向上的歸併排序 由於沒有用到數組下標 因此可以用於鏈表排序O(n*logn)
|-快速排序
選定元素挪到正確位置,挪動的過程也是分離大於選定元素 和 小於選定元素的過程
遞歸選定元素兩側的數組進行快排
當集合完全有序時 快拍退化成了O(n^2)級別 這是可以通過隨機選取每次快排的樞軸來優化
|-二路快排
當集合中的元素大量重複時 相等於temp的元素很多 導致數據集合分成了兩個不平衡的部分 ,改進patition函數的切分方式,變成雙路快排
|-三路快排
分割時將集合分成三部分
=temp
<temp
>temp
遞歸的分割後兩個部分
總結: 歸併排序 和 快速排序 都使用了分治算法
擴展應用:
實現方法見我的github: https://github.com/lzneu/Algrithm_python
|- 求逆序對
完全有序的數列 逆序對數量爲0 逆序的數列 逆序對數量最多
可以通過求逆序對的數量來表示數列的有序程度
暴力解法O(n^2)
歸併排序求逆序對 O(n*logn) swap一次 說明有一個逆序對 可以疊加
|- 求數組的第N大元素
利用快速排序 patition只留下 符合要求的那部分 知道 temp就是第n個
複雜度O(n)
import datetime
import random
import numpy as np
# 生成一個近乎有序的數組
def genNearlyOrderArray(n, swapTimes):
arr = list(range(n))
for i in range(swapTimes):
x = random.randint(0, n)
y = random.randint(0, n)
swap(arr, x, y)
return arr
def genRandomArray(n, start=0, end=10000):
return np.random.randint(start, end, size=n)
def aTestSort(sortName, arr, n):
t_start = datetime.datetime.now()
sortName(arr, n)
t_end = datetime.datetime.now() # 記錄函數結束時間)
long = (t_end - t_start).total_seconds()
if isSorted(arr, n):
print("sortName: %s, time: %f s" % (sortName.__name__, long))
else:
print('Sort ERROR!')
def swap(arr, i, j):
temp = arr[i]
arr[i] = arr[j]
arr[j] = temp
def isSorted(arr, n):
for i in range(n - 1):
if (arr[i] > arr[i + 1]):
return False
return True
# 將arr[l...mid] 和 arr[mid+1...r]兩部分合並
def __merge(arr, l, mid, r):
aux = arr[l: r + 1]
i = l
j = mid + 1
for k in range(l, r + 1):
if (i > mid):
arr[k] = aux[j - l]
j += 1
elif (j > r):
arr[k] = aux[i - l]
i += 1
elif (aux[i - l] < aux[j - l]):
arr[k] = aux[i - l]
i += 1
else:
arr[k] = aux[j - l]
j += 1
def insertionSort4Ms(arr, l, r):
# if l >= r:
# return
for i in range(l + 1, r + 1):
j = i
temp = arr[i]
while ((j > l) and (arr[j - 1] > temp)):
arr[j] = arr[j - 1]
j -= 1
arr[j] = temp
return
# 遞歸的使用歸併排序arr[l...r]
def __mergeSort(arr, l, r):
# if l >= r:
# return
# 此處優化 在n比較小時 調用插入排序
if (r - l) <= 15:
insertionSort4Ms(arr, l, r)
return
mid = int((l + r) / 2)
__mergeSort(arr, l, mid)
__mergeSort(arr, mid + 1, r)
# 若有序 無需再合併了
if (arr[mid] > arr[mid + 1]):
__merge(arr, l, mid, r)
def mergeSort(arr, n):
# 表示私有
__mergeSort(arr, 0, n - 1)
# 自底向上歸併排序(由於沒有用到數組下標 因此可以用於鏈表排序)
def mergeSortBU(arr, n):
size = 1
while (size <= n):
i = 0
while (i + size < n):
__merge(arr, i, i + size - 1, min(i + size + size - 1, n - 1))
i += (size + size)
size += size
# 對arr[l...r]進行partition
def __partition(arr, l, r):
# 此處對快排進行優化,隨機中選取元素作爲樞軸
swap(arr, l, random.randint(l, r))
temp = arr[l]
j = l
for i in range(l + 1, r + 1):
if (temp > arr[i]):
swap(arr, j + 1, i)
j += 1
swap(arr, j, l)
return j
# 對arr[l...r]進行快速排序
def __quickSort(arr, l, r):
# if (l>=r):
# return # 別忘了這個啊 要不然死循環了
if (r - l) <= 15:
# 元素個數少的時候用插入排序
insertionSort4Ms(arr, l, r)
return
p = __partition(arr, l, r)
__quickSort(arr, l, p - 1)
__quickSort(arr, p + 1, r)
# 快速排序
def quickSort(arr, n):
__quickSort(arr, 0, n - 1)
def __partition2(arr, l, r):
swap(arr, l, random.randint(l, r))
temp = arr[l]
i = l + 1
j = r
while True:
while (i <= r and arr[i] < temp):
i += 1
while (j >= l + 1 and arr[j] > temp):
j -= 1
if (j < i):
break
swap(arr, j, i)
i += 1
j -= 1
# temp所在的位置是<=temp的一段 因此需要將其與j交換
swap(arr, l, j)
return j
def __quickSort2(arr, l, r):
if (r - l) <= 15:
insertionSort4Ms(arr, l, r)
return
p = __partition2(arr, l, r)
__quickSort2(arr, l, p - 1)
__quickSort2(arr, p + 1, r)
# 二路快排 將=temp的元素分散到兩個集合中,避免平衡樹不平衡
def quickSort2(arr, n):
__quickSort2(arr, 0, n - 1)
def __partition3Ways(arr, l, r):
swap(arr, l, random.randint(l, r))
temp = arr[l]
lt = l # arr[l+1...lt] < temp
gt = r + 1 # arr[gt...r] > temp
i = l + 1 # arr[lt+1...i] == temp
while (i < gt):
# i==gt時表示已經比較結束
if (arr[i] < temp):
swap(arr, i, lt + 1)
lt += 1
i += 1
elif (arr[i] > temp):
swap(arr, i, gt - 1)
gt -= 1
else: # arr[i] == temp
i += 1
swap(arr, l, lt)
return lt, gt
def __quickSort3Ways(arr, l, r):
if (r - l) <= 15:
insertionSort4Ms(arr, l, r)
return
lt, gt = __partition3Ways(arr, l, r)
__quickSort3Ways(arr, l, lt - 1)
__quickSort3Ways(arr, gt, r)
def quickSort3Ways(arr, n):
__quickSort3Ways(arr, 0, n-1)
if __name__ == '__main__':
n = 100000
start = 0
end = 10000
arr = genNearlyOrderArray(n, swapTimes=100)
arr = genRandomArray(n, start, end)
arr2 = arr.copy()
arr3 = arr.copy()
arr4 = arr.copy()
aTestSort(mergeSort, arr, n)
# aTestSort(quickSort, arr2, n)
aTestSort(quickSort2, arr3, n)
aTestSort(quickSort3Ways, arr4, n)