堆排序的平均效率虽然小于快排,但是堆排序仍然有很多应用。堆排序的典型应用就是作为作业的调度系统,系统运行过程中可以随时INSERT一个新的任务。linux内核中的
sort
函数也是用的堆排序:
作者:知乎用户
链接:https://www.zhihu.com/question/20842649/answer/70922215
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
/**
* sort - sort an array of elements
* @base: pointer to data to sort
* @num: number of elements
* @size: size of each element
* @cmp_func: pointer to comparison function
* @swap_func: pointer to swap function or NULL
*
* This function does a heapsort on the given array. You may provide a
* swap_func function optimized to your element type.
*
* Sorting time is O(n log n) both on average and worst-case. While
* qsort is about 20% faster on average, it suffers from exploitable
* O(n*n) worst-case behavior and extra memory requirements that make
* it less suitable for kernel use.
*/
优先队列(Priority Queue)则是堆排序的另一种应用。
下面介绍一下优先队列操作的实现。
MAXIMUM
原理
最大值的原理很简单,就是返回根结点的
k
e
y
key
key。
MAXIMUM
return A[1]
代码实现
//HEAP_MAXIMUM
int HEAP_MAXIMUM(HEAP *A)
{
return A->Array[0];
}
EXTRACT-MAX
原理
最大值提取的意思是将二叉堆中的最大元素提取出来,此时二叉堆长度将减少,并且剩下的二叉堆将维持最大堆的性质。
EXTRACT-MAX
swap(A[1], A[A.heap-size])
A.heap-size-1
MAX-HEAPIFY(A, 1)
return A[A.heap-size+1]
代码实现
//HEAP_EXTRACT_MAXIMUM
int HEAP_EXTRACT_MAXIMUM(HEAP *A)
{
if(A->heap_size<0)
{
ERROR("heap underfloor");
}
else
{
SWAP(&A->Array[0], &A->Array[A->array_size-1]);
A->heap_size--;
MAX_HEAPIFY(A, 0);
return A->Array[A->array_size-1];
}
}
INCREASE
原理
增大键值函数可以将二叉堆中某一结点键值增大,同时还要维持最大堆的性质。因为是在最大堆的基础上进行的,所以只需要关注改变的结点和其父节点就可以了。
INCREASE(A, i, key)
if key<A[i]
ERROR("new value is smaller than before one")
else
A[i] = key
while i>1 and A[i]>A[PARENT(i)]
swap(A[i], A[PARENT(i)])
i = PARENT(i)
代码实现
//HEAP_INCREASE_KEY
void HEAP_INCREASE_KEY(HEAP *A, uint32_t i, int key)
{
if(key < A->Array[i-1])
{
ERROR("new key is smaller than the current one");
}
else
{
A->Array[i-1] = key;
while(i>1 && A->Array[PARENT(i)-1]<key)
{
SWAP(&A->Array[i-1], &A->Array[PARENT(i)-1]);
i=PARENT(i);
}
}
}
在头文件中定义一个ERROR函数用以实现错误信息的输出:
#define ERROR(s) printf("!!!ERROR:%s!!!\n", s)
INSERT
原理
插入是先插入
−
∞
-\infty
−∞,然后再将其增大的想要的键值。之所以这样做,是为了少执行一遍建堆的过程,而只INCREASE即可。
INSERT(A, key)
A.heap-size += 1
A[heap-size] = -inf
INCREASE(A, heap-size, key)
代码实现
//MAX_HEAP_INSERT
void MAX_HEAP_INSERT(HEAP *A, int key)
{
A->heap_size++;
A->Array[A->heap_size-1] = negINFINITY;
HEAP_INCREASE_KEY(A, A->heap_size, key);
}
DELETE
原理
删除元素时要注意二叉堆的长度要对应减少。
DELETE(A, i)
swap(A[i], A[A.heap-size])
A.heap-size -= 1
BUILD-MAX-HEAPIFY(A)
代码实现
//6.5-8
//MAX_HEAP_DELETE
void MAX_HEAP_DELETE(HEAP *A, uint32_t i)
{
SWAP(&A->Array[i-1], &A->Array[A->heap_size-1]);
A->heap_size--;
BUILD_MAX_HEAP(A);
}
测试函数
以上功能的测试函数我把它们放到一起了:
//------------------------- priority queue -------------------------//
//TEST FUNCTION
void testPriorityQueue()
{
//6.5-1
int testArray[12] = {15, 13, 9, 5, 12, 8, 7, 4, 0, 6, 25, 100};
uint32_t array_size = 12;
HEAP A = {12, array_size, testArray};
//bulid max heap
BUILD_MAX_HEAP(&A);
drawBINTREE(A.Array, A.heap_size);
//maximum
printf("the maximum value of heap is %d\n", HEAP_MAXIMUM(&A));
//extract maximum
HEAP_EXTRACT_MAXIMUM(&A);
HEAP_EXTRACT_MAXIMUM(&A);
printf("bintree after extract:\n");
drawBINTREE(A.Array, A.heap_size);
//increase key
HEAP_INCREASE_KEY(&A, 7, 10);
printf("bintree after increase:\n");
drawBINTREE(A.Array, A.heap_size);
//insert key
MAX_HEAP_INSERT(&A, 11);
printf("bintree after insert:\n");
drawBINTREE(A.Array, A.heap_size);
//6.5-8
//delete element
MAX_HEAP_DELETE(&A, 2);
printf("bintree after delete:\n");
drawBINTREE(A.Array, A.heap_size);
}
得到的结果如下:
The height of this tree is: 4
100
25 15
5 13 9 7
4 0 6 12 8
the maximum value of heap is 100
bintree after extract:
The height of this tree is: 4
100
13 15
5 12 9 7
4 0 6
bintree after increase:
The height of this tree is: 4
100
13 15
5 12 9 10
4 0 6
bintree after insert:
The height of this tree is: 4
100
13 15
5 12 9 10
4 0 6 11
bintree after delete:
The height of this tree is: 4
100
12 15
5 11 9 10
4 0 6