1 快速排序
快速排序使用了分治思想,分治的三个过程如下:
A 分解
数组A[p..r]被划分为两个(可能为空)子数组A[p..q-1]和A[q+1..r],使得A[p..q-1]中的每一个元素都小于等于A[q],而A[q]也小于等于A[q+1..r]中的每一个元素。其中计算下标q也是算法的一部分。
B 解决
通过递归调用快速排序,对子数组A[p..q-1],A[q+1..r]进行排序
C 合并
因为子数组都是原址排序的,不需要合并
下面是快速排序算法的实现(算法导论(95页快速排序))
QUICKSORT(A, p, r)
if p < r
q = PARTTION(A, p, r)
QUICKSORT(A, p, q-1)
QUICKSORT(A, q+1, p)
计算下标
PARTTION(A, p, r)
x = A[r]
i = p - 1
for j = p to r-1
if A[j] <= x
i = i+1
exchange A[i] with A[j]
exchange A[i+1] with A[r]
return i+1
写了一个例子,追踪地址:
https://github.com/hk2413435641/codes/blob/master/algorithm/quick_sort.c
快速排序的平均时间复杂度为O(nlgn),最坏的情况下是O(n2),快速排序可能是实际中最好的选择,因为它还支持原址排序
2 堆排序
堆排序算法使用的是最大堆作为容器的。堆可以看成是一颗二叉树,这颗二叉树除了最底层外,其它层都是满的。所以如果一个节点索引为[i],则父节点索引为[i/2],左孩子节点为[2i],右孩子节点为[2i+1]。堆分为最大堆和最小堆。最大堆指的是父节点都要比自己的孩子节点大,最小堆相反,所有的父节点都要比自己的孩子节点小。其中,堆排序使用最大堆作为数据容器。优先队列使用的是最小堆。
堆排序的步骤:
1 维持最大堆结构
2 建堆
3 堆排序
维持最大堆结构的伪代码:
MAX-HEAPIFY (A, i)
l = LEFT(i)
R = RIGHT(i)
if l <= A.heap_size && A[l] > A[i]
largest = l;
else largest = i;
if r <= A.heap_size && A[r] > A[largest]
largest = r;
if largest != i
exchange A[i] with A[largest]
MAX-HEAPIFY(A, largest)
建堆的伪代码:
BUILD-MAX-HEAP(A)
A.heap_size = A.length
for i = [A.length/2] downto 1
MAX-HEAPIFY(A, i)
堆排序的伪代码:
HEAPSORT(A)
BUILD-MAX-HEAP(A)
for i = A.length downto 2
exchange A[1] with A[i]
A.heap_size = A.heap_size - 1
MAX-HEAPIFY(A, 1)
实现代码:https://github.com/hk2413435641/codes/blob/master/algorithm/heap_sort.c