文章目录
概要
接上回
在上篇说过经典的排序算法,有冒泡,插入,选择;归并,快排。其中讲了冒泡,插入,选择;这一回写归并和快排。
原理及实现
原理是很好玩的,如果不喜欢,先记住,总有一天会用得上的。
原理挺有意思的,喜欢就去研究,不喜欢就记住一些常用的,以备不时之需。先聊聊归并吧。
归并排序
本来想找个定义,看了下维基百科,也不是很满意的概念,如下:
定义
归并排序(英语:Merge sort,或mergesort),是建立在归并操作上的一种有效的排序算法,效率为𝑂(𝑛log𝑛)(大O符号)。1945年由约翰·冯·诺伊曼首次提出。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用,且各层分治递归可以同时进行。
定义是这样的,怎么理解呢?采用分治递归,意思就是先平均分成2份,然后比较排序;递归这种操作,就是将2份中任何一份继续采用分治法(分治法这里不细聊,以后会写文章的),分成2份,直到不能再分为止;
性能
- 归并排序是稳定的排序算法;数组长度就那么长,一直在分割,然后继续分割直到不能再分,然后采用合并数组;
- 时间复杂度:O(nlogn)
- 空间复杂度:O(nlogn)
代码Python
python
dfrom typing import List
def merge_sort(a: List[int]):
merge_sort_between(a, 0, len(a) - 1)
def merge_sort_between(a: List[int], low: int, high: int):
# The indices are inclusive for both low and high.
if low < high:
mid = low + (high - low) // 2
merge_sort_between(a, low, mid)
merge_sort_between(a, mid + 1, high)
merge(a, low, mid, high)
def merge(a: List[int], low: int, mid: int, high: int):
# a[low:mid], a[mid+1, high] are sorted.
i, j = low, mid + 1
tmp = []
while i <= mid and j <= high:
if a[i] <= a[j]:
tmp.append(a[i])
i += 1
else:
tmp.append(a[j])
j += 1
start = i if i <= mid else j
end = mid if i <= mid else high
tmp.extend(a[start:end + 1])
a[low:high + 1] = tmp
快速排序
归并说完了,来看看快排。定义如下:
定义
快速排序(英语:Quicksort),又称分区交换排序(partition-exchange sort),是一种排序算法,最早由东尼·霍尔提出。在平均状况下,排序𝑛个项目要𝑂(𝑛log𝑛)(大O符号)次比较。在最坏状况下则需要𝑂( n 2 n^2 n2)次比较,但这种状况并不常见。事实上,快速排序Θ(𝑛log𝑛)通常明显比其他算法更快,因为它的内部循环可以在大部分的架构上很有效率地达成。
按照定义,对一定长度的数据,选一个基准值,然后所有数据一个一个跟基准值比较,然后交互i,j的位置。还是分治和递归的思想,一直这么分,partion。
接下来看看代码,如下:
代码
python
from typing import List
import random
def quick_sort(a: List[int]):
quick_sort_between(a, 0, len(a) - 1)
def quick_sort_between(a: List[int], low: int, high: int):
if low < high:
# get a random position as the pivot
k = random.randint(low, high)
a[low], a[k] = a[k], a[low]
m = partition(a, low, high) # a[m] is in final position
quick_sort_between(a, low, m - 1)
quick_sort_between(a, m + 1, high)
def partition(a: List[int], low: int, high: int):
pivot, j = a[low], low
for i in range(low + 1, high + 1):
if a[i] <= pivot:
j += 1
a[j], a[i] = a[i], a[j] # swap
a[low], a[j] = a[j], a[low]
return j
小结
学习总结
归并,排序都采用了分治的思想。时间复杂度归并O(nlogn),空间复杂度归并的比较大,达到O(n);快排时间复杂度O(nlogn),快排极端情况达到O( n 2 n^2 n2),但是概率很小,而且可以通过合理的选用基准值来避免,适用范围较广。