堆(1)--堆排序HeapSort

1. 堆或称二叉堆

二叉堆是完全二叉树或者是近似完全二叉树。 二叉堆满足二个特性: 1.父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值。 2.每个结点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆)。 当父结点的键值总是大于或等于任何一个子节点的键值时为最大堆。 当父结点的键值总是小于或等于任何一个子节点的键值时为最小堆。

堆一般都用数组来表示, 若数组下标从0开始, i 结点的父结点下标PARENT(i)就为(i – 1) / 2, 它的左右子结点下标LEFT(i)和RIGHT(i)分别为 2 * i + 1 和 2 * i + 2, 如第0个结点左右子结点下标分别为1和2。

最大堆, 或称大根堆, A[PARENT(i)] >= A[i], 常用于升序排序, 也可以实现最大优先级队列, 用来作业调度; 最小堆, 或称小根堆, A[PARENT(i)] <= A[i], 常用于优先级队列, 可以实现最小优先队列,用于事件驱动的模拟器,发生时间为关键字,事件必须按照发生的时间顺序进行模拟;

《堆(1)--堆排序HeapSort》

2. 算法

/*维护最大堆O(lgn)
假设数据下标从1开始, 假设根节点为LEFT(i)和RIGHT(i)的二叉树都是最大堆, 
但A[i]有可能小于其孩子, 需要逐级下沉。*/
MAX-HEAPIFY(A, i) 
    l = LEFT(i)
    r = RIGHT(i)
    if l<= A.heap-size and A[l] > A[i]
        largest = l
    else largest = i
    if r <= A.heap-size and A[r] > A[largest]
        largest = r
    if largest  !=  i
        exchange A[i] with A[largest]
        MAX-HEAPIFY(A, largest)

/* 构造最大堆
子数组A([n/2]+1..n)中的元素都是树的叶子结点。
每个叶结点都可以看成只包含一个元素的堆。*/
BUILD-MAX-HEAP(A) 
    A.heap-size = A.length
    for i=[A.length/2] downto 1
        MAX-HEAPIFY(A, i)

/* 堆排序 O(nlgn)
初始时候,堆排序算法利用BUILD-MAX-HEAP将输入数组A[1..n]建成最大堆,其中n=A.length。
因为数组中的最大元素总在根结点A[1]中,通过把它与A[n]进行互换,我们可以让该元素放在正确的位置。
这时候,如果我们从堆中去掉结点n,剩余的结点中,原来根的孩子结点仍然是最大堆,而新的根结点可能会违背最大堆的性质。为了维护最大堆的性质,要调用MAX-HEAPIFY(A,1),从而在A[1..n-1]上构造一个新的最大堆。
堆排序算法会不断重复这一过程,直到堆的大小从n-1降到2。*/
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)

//--------------------------以下操作用于最大优先级队列---------------------------
/* 返回最大值 */
HEAP-MAXIMUM(A)
    return A[1]

/* 返回并删除最大值 */
HEAP-EXTRACT-MAX(A)
    if A.heap-size < 1
        error "heap underflow"
    max=A[1]
    A[1] = A[A.heap-size]
    A.heap-size = A.heap-size-1
    MAX-HEAPIFY(A, 1)
    return max

/* 将i处的值增加到key,假设key不小于i处的原值 */
HEAP-INCREASE-KEY(A,i,key)
    if key < A[i]
        error "new key is smaller than current key"
    A[i] = key
    while i>1 and A[PARENT(i) < A[i]
        exchange A[i] with A[PARENT(i)]
        i=PARENT(i)

/* 插入一个元素key */
MAX-HEAP-INSERT(A, key)
    A.heap-size = A.heap-size+1
    A[A.heap-size] = negative-infinite
    HEAP-INCREASE-KEY(A, A.heap-size, key)

3. 堆排序

使用最大堆升序排序,时间复杂度O(nlogn), 不稳定的排序。根据算法写的代码:

#include<stdio.h>

void swap(int *x, int *y)
{
	int t = *x;
	*x = *y;
	*y = t;
}

int parent(int i) {
	if (i == 0)
		return 0;
	return (i - 1) / 2;
}

int left(int i) {
	return 2 * i + 1;
}

int right(int i) {
	return 2 * i + 2;
}

void max_heapify(int a[], int n, int i) {
	int l = left(i);
	int r = right(i);
	int largest;
	if (l <= n && a[l] > a[i])
		largest = l;
	else
		largest = i;

	if (r <= n && a[r] > a[largest])
		largest = r;
	if (largest != i) {
		swap(&a[i], &a[largest]);
		max_heapify(a, n, largest);
	}
}

void build_max_heap(int a[], int n) {
	int heap_size = n;
	int i;
	for (i = heap_size / 2 - 0; i >= 0; i--)
		max_heapify(a, n, i);
}

void heapsort(int a[], int n) {
	build_max_heap(a, n);
	int i;
	int heap_size = n;
	for (i = n - 1; i >= 1; i--) {
		swap(&a[0], &a[i]);
		heap_size--;
		max_heapify(a, heap_size, 0);
	}
}

int main(int argc, char * argv[])
{
	int a[8] = { 8, 7, 6, 5, 4, 3, 2, 1 };
	int i;
	heapsort(a, 8);
	for (i = 0; i < 8; i++)
		printf("%d ", a[i]);
	printf("\n");
	return 0;
}

点赞