源代码:
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)
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)