二叉堆是一种优先队列的数据结构,具有2种性质:结构性质和堆序性。这里讨论都基于最小二叉堆,这种二叉堆对最小元素的访问非常高效。
二叉堆的ADT操作主要包括Insert(插入)和DeleteMin(删除最小元)。
1、结构性质:堆是一棵完全二叉树(若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 都被填满,第 h 层所有的结点都连续集中在最左边),如下图。
(1)因为完全二叉树很有规律,因此可以用一个数组而不需要用指针存储,可以存储成如下形式,其中[0]地址处元素常常用于标记(对于最小二叉堆而言,存储一个非常小的值),这只是为了编程方便,当然也可以不用该标记。
(2)对于数组中任一位置i处的元素,其左儿子在位置2*i上,右儿子在2*i+1上。这条信息很有用,也正因为这样,我们可以很方便的不用指针而只用数组就能访问左右儿子。
2、堆序性:
由于我们想快速的找到最小元,因此最小元应在根上。我们可以以O(1)时间找到最小值。
堆序性指,在一个堆中,每一个节点X,X父亲中的关键字小于(或等于)X中的关键字,根节点除外(因为没有父亲)。
3、插入
为将一个元素 X 插入到堆中,我们在下一个可用位置创建一个空穴,否则该堆将不是完全数。如果 X 可以放在该空穴中而不破坏堆的序,那么插入完成。否则,我们把空穴的父节点上的元素移入该空穴中,这样,空穴就朝着根的方向上冒一步。继续改过程直到 X 能被放入空穴中为止。
这样一般的策略叫做上滤( percolate up);新元素在堆中上滤直到找出正确的位置。
4、删除最小元
当删除一个最小元时,要在根节点建立一个空穴。由于现在堆少了一个元素,因此堆中最后一个元素 X 必须移动到该堆的某个地方。如果 X 可以直接被放到空穴中,那么 deleteMin 完成。不过这一般不太可能,因此我们将空穴的两个儿子中比较小者移入空穴,这样就把空穴向下推了一层。重复该步骤直到 X 可以被放入空穴中。因此,我们的做法是将 X 置入沿着从根开始包含最小儿子的一条路径上的一个正确的位置。
这种一般的策略叫做下滤(percolate down)。
5、程序代码实例BinHeap.h(types.h中只是些重定义)
/*
* =====================================================================================
*
* Filename: BinHeap.h
*
* Description: 二叉堆上滤和下滤的例程
*
* Version: 1.0
* Created: 2012/12/2 19:20:45
* Revision: none
* Compiler: gcc
*
* Author: xhzuoxin (QQ:1126804077), [email protected]
* Organization:
*
* =====================================================================================
*/
#ifndef _BINHEAP_H
#define _BINHEAP_H
#include "../types.h"
typedef UINT16 EleType;
typedef struct HeapStruct
{
int cur;
int size;
EleType *ele;
}Heap;
typedef Heap* PtrHeap;
typedef UINT16 Position;
/* functions */
extern PtrHeap InitHeap(PtrHeap h, int max_ele);
extern PtrHeap InsertHeap(PtrHeap h, EleType x);
extern EleType DelMinHeap(PtrHeap h);
extern PtrHeap BuildHeap(EleType *x, int n);
extern PtrHeap DecreaseKey(Position pos, int delta, PtrHeap h);
extern PtrHeap IncreaseKey(Position pos, int delta, PtrHeap h);
extern EleType Delete(Position pos, PtrHeap h);
#endif
BinHeap.c
/*
* =====================================================================================
*
* Filename: BinHeap.c
*
* Description:
*
* Version: 1.0
* Created: 2012/12/2 20:17:44
* Revision: none
* Compiler: gcc
*
* Author: xhzuoxin (QQ:1126804077), [email protected]
* Organization:
*
* =====================================================================================
*/
#include
#include
#include "BinHeap.h"
#define ERR_MSG(x) printf(x)
#define MIN_DATA (0)
/*
* === FUNCTION ======================================================================
* Name: PercolateDown
* Description: 空穴下滤操作
* =====================================================================================
*/
static void PercolateDown(PtrHeap h, UINT16 hole)
{
UINT16 i = 0;
UINT16 child = 0;
EleType temp = 0;
if(NULL == h)
{
return;
}
temp = h->ele[hole];
i = hole;
child = 2 * i;
while(child <= h->cur)
{
if(child != h->cur) // have left+right child
{
if(h->ele[child+1] < h->ele[child])
{
child = child + 1;
}
}
// compare to h->ele[hole]
if(temp > h->ele[child])
{
h->ele[i] = h->ele[child];
}
else
{
break;
}
i = child;
child = 2 * i;
}
h->ele[i] = temp;
}
/*
* === FUNCTION ======================================================================
* Name: PercolateUp
* Description: 上滤操作
* =====================================================================================
*/
static void PercolateUp(PtrHeap h, UINT16 hole)
{
EleType temp = h->ele[hole];
UINT16 i = 0;
UINT16 parent = 0;
i = hole;
parent = i >> 1;
while(temp < h->ele[parent])
{
h->ele[i] = h->ele[parent];
i = parent;
parent = i >> 1;
}
h->ele[i] = temp;
}
/*
* === FUNCTION ======================================================================
* Name: InitHeap
* Description:
* =====================================================================================
*/
PtrHeap InitHeap(PtrHeap h, int max_ele)
{
if(h != NULL)
{
if(h->ele != NULL)
{
free(h->ele);
}
free(h);
}
h = (PtrHeap)malloc(sizeof(Heap));
if(NULL == h)
{
ERR_MSG("out of space!\n");
return NULL;
}
h->ele = (EleType *)malloc(max_ele*sizeof(EleType));
if(NULL == h->ele)
{
ERR_MSG("out of space!\n");
return NULL;
}
h->size = max_ele;
h->cur = 0;
h->ele[0] = MIN_DATA;
return h;
}
/*
* === FUNCTION ======================================================================
* Name: InsertHeap
* Description: insert is a percolate up operator
* =====================================================================================
*/
PtrHeap InsertHeap(PtrHeap h, EleType x)
{
int i = 0;
if(h->cur >= h->size-1)
{
ERR_MSG("Heap is full!\n");
return NULL;
}
for(i = ++h->cur; h->ele[i>>1] > x; i >>= 1)
{
h->ele[i] = h->ele[i>>1];
}
h->ele[i] = x;
return h;
}
/*
* === FUNCTION ======================================================================
* Name: DelMinHeap
* Description:
* =====================================================================================
*/
EleType DelMinHeap(PtrHeap h)
{
int i = 0;
int child = 0;
EleType min_ele, last_ele;
if(h->cur == 0) /* empty heap */
{
ERR_MSG("Heap is empty!\n");
return h->ele[0];
}
min_ele = h->ele[1];
last_ele = h->ele[h->cur];
h->cur--;
i = 1;
child = 2 * i;
while(child <= h->cur)
{
if((child < h->cur) && (h->ele[child+1] < h->ele[child]))
{
child++;
}
/* compare to last_element */
if(h->ele[child] < last_ele)
{
h->ele[i] = h->ele[child];
}
else
{
break;
}
i = child;
child = 2 * i;
}
h->ele[i] = last_ele;
return min_ele;
}
/*
* === FUNCTION ======================================================================
* Name: BuildHeap
* Description: 从数组中建立堆的例程
* =====================================================================================
*/
PtrHeap BuildHeap(EleType *x, int n)
{
PtrHeap h = NULL;
int i = 0;
h = InitHeap(h, n*2); /* include ele[0]=0 */
for(i=0; iele[i+1] = x[i];
}
h->cur = n;
for(i=n/2; i>0; i--)
{
PercolateDown(h, i);
}
return h;
}
/*
* === FUNCTION ======================================================================
* Name: IncreaseKey
* Description: 增加关键字的值
* pos -- 关键字在堆中的位置
* delta -- 增幅
* h -- 堆
* =====================================================================================
*/
PtrHeap IncreaseKey(Position pos, int delta, PtrHeap h)
{
// increase pos
h->ele[pos] += delta;
PercolateDown(h, pos);
return h; // also needn't this line
}
/*
* === FUNCTION ======================================================================
* Name: DecreaseKey
* Description: 降低关键字的值
* pos -- 关键字在堆中的位置
* delta -- 降幅
* h -- 堆
* =====================================================================================
*/
PtrHeap DecreaseKey(Position pos, int delta, PtrHeap h)
{
// decrease pos
h->ele[pos] -= delta;
PercolateUp(h, pos);
return h;
}
/*
* === FUNCTION ======================================================================
* Name: Delete
* Description: Delete a element in Heap.
* First, use DecreaseKey(P, Infinite, H);
* Second, use DeleteMin(H)
* =====================================================================================
*/
EleType Delete(Position pos, PtrHeap h)
{
EleType temp = h->ele[pos];
DecreaseKey(pos, h->ele[pos], h); // set ele[POS] want to delete to 0
if(0 != DelMinHeap(h))
{
return 0;
}
return temp;
}
/*
* =====================================================================================
*
* Filename: TestSuite.c
*
* Description: test file
*
* Version: 1.0
* Created: 2012/12/3 14:03:34
* Revision: none
* Compiler: gcc
*
* Author: xhzuoxin (QQ:1126804077), [email protected]
* Organization:
*
* =====================================================================================
*/
#include
#include
#include "CUnit/Console.h"
#include "BinHeap.h"
PtrHeap h = NULL;
int InitSuite(void)
{
if(NULL == (h=InitHeap(h, 30)))
{
return -1;
}
else
{
return 0;
}
return 0;
}
int EndSuite(void)
{
free(h->ele);
free(h);
return 0;
}
void TestInsertHeap(void)
{
int i = 0;
if(NULL != h)
{
CU_ASSERT(NULL != InsertHeap(h, 50));
CU_ASSERT(NULL != InsertHeap(h, 100));
CU_ASSERT(NULL != InsertHeap(h, 200));
}
printf("\n InsertHeap: ");
for(i=1; i<=h->cur; i++)
{
printf("%d ", h->ele[i]);
}
}
void TestDelMinHeap(void)
{
printf("\n DelMin: ");
while(h->cur > 0)
{
printf("%d ", DelMinHeap(h));
}
}
void TestBuildHeap(void)
{
int i = 0;
EleType x[15] = {10, 12, 1, 14, 6, 5, 8, 15, 3, 9, 7, 4, 11, 13, 2};
h = BuildHeap(x, 15);
printf("\n BuildHeap:");
for(i=1; i<=h->cur; i++)
{
printf("%d ", h->ele[i]);
}
}
void TestIncreaseKey(void)
{
int pos = 5;
int delta = 50;
int i = 0;
h = IncreaseKey(pos, delta, h);
printf("\n IncreaseKey:");
for(i=1; i<=h->cur; i++)
{
printf("%d ", h->ele[i]);
}
}
void TestDecreaseKey(void)
{
int pos = 5;
int delta = 1;
int i = 0;
h = DecreaseKey(pos, delta, h);
printf("\n DecreaseKey:");
for(i=1; i<=h->cur; i++)
{
printf("%d ", h->ele[i]);
}
}
void TestDelete(void)
{
int pos = 5;
EleType e = Delete(pos, h);
printf("\n Delete: %d", e);
}
int main(void)
{
// CU_pSuite suite = NULL;
//
// /* Init registry */
// if(CUE_SUCCESS != CU_initialize_registry())
// {
// return CU_get_error();
// }
//
// /* add suite */
// suite = CU_add_suite("suite1", InitSuite, EndSuite);
// if(NULL == suite)
// {
// CU_cleanup_registry();
// return CU_get_error();
// }
//
// /* add tests */
// if(NULL == CU_add_test(suite, "test of InsertHeap()", TestInsertHeap)
// || NULL == CU_add_test(suite, "test of DelMinHeap()", TestDelMinHeap))
// {
// CU_cleanup_registry();
// return CU_get_error();
// }
//
// /* run */
// CU_console_run_tests();
//
// /* cleanup */
// CU_cleanup_registry();
CU_TestInfo testcase[] = {
{"TestBuild", TestBuildHeap},
{"TestInsert", TestInsertHeap},
{"TestIncreaseKey", TestIncreaseKey},
{"TestDecreaseKey", TestDecreaseKey},
{"TestDelete", TestDelete},
{"TestDelMin", TestDelMinHeap},
CU_TEST_INFO_NULL
};
CU_SuiteInfo suite[] = {
{"Testing func ", InitSuite, EndSuite, testcase},
CU_SUITE_INFO_NULL
};
/* Init registry */
if(CUE_SUCCESS != CU_initialize_registry())
{
return CU_get_error();
}
/* register suite */
if(CUE_SUCCESS != CU_register_suites(suite))
{
return 1;
}
CU_console_run_tests();
CU_cleanup_registry();
return 0;
}
图6-15中的第一棵树是无序树。从图6-15到图6-18中其余7棵树表示出 7 个 percolateDown 中每一个的执行结果。每条虚线对应两次比较:一次是找出较小的儿子节点,另一个是较小的儿子与该节点的比较。注意,在整个算法中只有 10 跳虚线,它们对应 20 次比较。
最终,使用gcc编译的makfile文件为
SRC=BinHeap.c BinHeap.h TestSuite.c
LIB=-L/usr/local/lib
INC=-I/usr/local/include
TestSuite:$(SRC)
gcc $^ -o $@ $(LIB) $(INC) -lcunit -g -Wall -static
.PHONY:clean tags
clean:
rm -f TestSuite *~
tags:
ctags -R --c++-kinds=+p --fields=+iaS --extra=+q
测试结果如下(正确):
测试案列运行过程:
(1)使用BuildHeap建立堆;
(2)尝试插入元素(50,100,200)到堆;
(3)增加pos=5处的关键字值;
(4)减小pos=5处关键字的值;
(5)Delete函数删除pos=5处的关键字值;
(6)逐个删除最小元直到堆为空
参考:
(1)《数据结构与算法分析——C语言描述》
(2)http://blog.csdn.net/gqltt/article/details/7718484
(3)http://program.sinaapp.com/?p=49计算机程序设计艺术*算法交流