上一篇介绍了堆排序的基本操作,这篇主要说说堆排序的主要应用。其实堆排序在几大排序算法里面并不算最优的一种,但是其存在就会有它的价值。下面我们来学习,用上一篇构造的最大堆设计一个最大优先级队列(priority queue)。
优先级队列是一个用来维护一组元素注册的集合S的数据结构,其中每一个元素都有一个相关的值,称为关键字KEY。最大优先级队列常用的操作:
INSERT(S, x):把元素x插入集合S中。
MAXIMUM(S):返回S集合中最大关键字的元素。
EXTRACT_MAXIMUM(S):去掉并返回S中的具有最大关键字的元素。
INCREASE-KEY(S, x, k):将元素x的关键字增加到k,这里k >= 元素x的关键字。
最大优先级队列其中一个应用是在操作系统里面作为任务调度的优先级队列。KEY值与任务优先级挂钩。操作系统利用优先级队列方便的进行任务切换删除添加等操作。最小优先级可以被用于基于事件驱动的模拟器。队列中元素存放要模拟的事件。一种方法是:每个事件都有一个发生的时间作为其关键字,每次TICK时钟遍历递减关键字,如果关键字为0就模拟对应的事件。另外一种是:模拟程序调用EXTRACT-MIN获取下一个要模拟的事件,当一个新事件产生时,模拟器通过INSERT把其插入最小优先级队列。需要说明的是:我们需要确定哪个对象对应一个给定的优先级队列元素,反之亦然。因此,在利用堆来实现优先级队列的时候,队列元素需要存放对应对象的句柄,同样,在应用程序里我们也需要存储一个对应元素的句柄。这些句柄需要我们自己正确维护。
MAXIMUM(S)实现:
int maxinum(int* pnArray, size_t nCount)
{
if(pnArray == NULL || nCount <= 0)
{
return (-1);
}
return (pnArray[0]);
}
HEAP-EXTRACT-MAX实现:
int heap_extract_max(int* pnArray, size_t nCount)
{
int nMaxVal = 0;
if(pnArray == NULL || nCount <= 0)
{
return (-1);
}
nMaxVal = pnArray[0];
pnArray[0] = pnArray[nCount - 1];
nCount = nCount - 1;
max_heapify(pnArray, nCount, 0);
return (nMaxVal);
}
HEAP-INCREASE-KEY:
int heap_increase_key(int* pnArray, int nIndex, int nDstKey)
{
if(pnArray == NULL || nIndex < 0)
{
return (-1);
}
if(nDstKey < pnArray[nIndex])
{
printf("new key is small than current key\n");
return (1);
}
pnArray[nIndex] = nDstKey;
while(nIndex > 0 && pnArray[PARENT_INDEX(nIndex)] < pnArray[nIndex])
{
_exchange_val(pnArray, nIndex, PARENT_INDEX(nIndex));
nIndex = PARENT_INDEX(nIndex);
}
}
MAX-HEAP-INSERT:
int max_heap_insert(int* pnArray, size_t nSize, int nNewKey)
{
if(pnArray == NULL || nSize < 0)
{
return (-1);
}
pnArray[nSize] = -1;
heap_increase_key(pnArray, nSize, nNewKey);
}
上面几篇文章都提到的几个宏:
#define PARENT_INDEX(idx) ((idx + 1) / 2 - 1)
#define LEFT_INDEX(idx) (2 * (idx + 1) - 1)
#define RIGHT_INDEX(idx) (2 * (idx + 1))
#define LEAF_START(heap_size) (heap_size / 2)
这样就完成了一个最大优先级队列的基本操作了,后面会写几个具体应用实例。——————————————————————The End。