什么是堆
优先队列(Priority Queue):特殊的“队列”,取出元素的顺序是依照元素的优先权(关键字)大小,而不是元素进入队列的先后顺序。
堆的两个特性
结构性:用数组表示的完全二叉树。
有序性:任一结点的关键字是其子树所有结点的最大值(或最小值)
- “最大堆(MaxHeap)”,也称“大顶堆”:最大值
- “最小堆(MinHeap)”,也称“小顶堆”:最小值
堆的抽象数据类型描述
类型名称:最大堆(MaxHeap)
数据对象集:完全二叉树,每个结点的元素值不小于其子结点的元素值
操作集:最大堆H∈MaxHeap,元素item∈ElementType,主要操作有:
MaxHeap Create(int MaxSize):创建一个空的最大堆
Boolean IsFull(MaxHeap H):判断最大堆H是否已满
Insert(MaxHeap H, ElementType item):将元素item插入最大堆H
Boolean IsEmpty(MaxHeap H):判断最大堆H是否为空
ElementType DeleteMax(MaxHeap H):返回H中最大元素(高优先级)
最大堆的创建
typedef struct HNode *Heap; /* 堆的类型定义 */ struct HNode { ElementType *Data; /* 存储元素的数组 */ int Size; /* 堆中当前元素个数 */ int Capacity; /* 堆的最大容量 */ }; typedef Heap MaxHeap; /* 最大堆 */ typedef Heap MinHeap; /* 最小堆 */
创建一个空的最大堆
#define MAXDATA 1000 /* 该值应根据具体情况定义为大于堆中所有可能元素的值 */ MaxHeap CreateHeap( int MaxSize ) { /* 创建容量为MaxSize的空的最大堆 */ MaxHeap H = (MaxHeap)malloc(sizeof(struct HNode)); H->Data = (ElementType *)malloc((MaxSize+1)*sizeof(ElementType)); H->Size = 0; H->Capacity = MaxSize; H->Data[0] = MAXDATA; /* 定义"哨兵"为大于堆中所有可能元素的值*/ return H; }
将item插入到堆H中
bool IsFull( MaxHeap H ) { return (H->Size == H->Capacity); } bool Insert( MaxHeap H, ElementType X ) { /* 将元素X插入最大堆H,其中H->Data[0]已经定义为哨兵 */ int i; if ( IsFull(H) ) { printf("最大堆已满"); return false; } i = ++H->Size; /* i指向插入后堆中的最后一个元素的位置 */ for ( ; H->Data[i/2] < X; i/=2 ) H->Data[i] = H->Data[i/2]; /* 上滤X */ H->Data[i] = X; /* 将X插入 */ return true; } #define ERROR -1 /* 错误标识应根据具体情况定义为堆中不可能出现的元素值 */
插入到最后一个位置,如果不符合,就和父结点换位置。如果继续不符合就继续和父结点换位置,直到符合。
最大堆的删除
bool IsEmpty( MaxHeap H ) { return (H->Size == 0); } ElementType DeleteMax( MaxHeap H ) { /* 从最大堆H中取出键值为最大的元素,并删除一个结点 */ int Parent, Child; ElementType MaxItem, X; if ( IsEmpty(H) ) { printf("最大堆已为空"); return ERROR; } MaxItem = H->Data[1]; /* 取出根结点存放的最大值 */ /* 用最大堆中最后一个元素从根结点开始向上过滤下层结点 */ X = H->Data[H->Size--]; /* 注意当前堆的规模要减小 */ for( Parent=1; Parent*2<=H->Size; Parent=Child ) { Child = Parent * 2; if( (Child!=H->Size) && (H->Data[Child]<H->Data[Child+1]) ) Child++; /* Child指向左右子结点的较大者 */ if( X >= H->Data[Child] ) break; /* 找到了合适位置 */ else /* 下滤X */ H->Data[Parent] = H->Data[Child]; } H->Data[Parent] = X; return MaxItem; }
最大堆的建立
建立最大堆:将已经存在的N个元素按最大堆的要求存放在一个一维数组中
方法1:通过插入操作,将N个元素一个个相继插入到一个初始为空的堆中去,其时间代价为O(NlogN)
方法2:在线性事件复杂度下建立最大堆
(1)将N个元素按输入顺序存入,先满足完全二叉树的结构特性
(2)调整各结点位置,以满足最大堆的有序特性
/*----------- 建造最大堆 -----------*/ void PercDown( MaxHeap H, int p ) { /* 下滤:将H中以H->Data[p]为根的子堆调整为最大堆 */ int Parent, Child; ElementType X; X = H->Data[p]; /* 取出根结点存放的值 */ for( Parent=p; Parent*2<=H->Size; Parent=Child ) { Child = Parent * 2; if( (Child!=H->Size) && (H->Data[Child]<H->Data[Child+1]) ) Child++; /* Child指向左右子结点的较大者 */ if( X >= H->Data[Child] ) break; /* 找到了合适位置 */ else /* 下滤X */ H->Data[Parent] = H->Data[Child]; } H->Data[Parent] = X; } void BuildHeap( MaxHeap H ) { /* 调整H->Data[]中的元素,使满足最大堆的有序性 */ /* 这里假设所有H->Size个元素已经存在H->Data[]中 */ int i; /* 从最后一个结点的父节点开始,到根结点1 */ for( i = H->Size/2; i>0; i-- ) PercDown( H, i ); }
最小堆相关的代码,和最大堆的类似
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 5 #define MINDATA -1000 6 #define ERROR -1 7 typedef struct HeapStruct *MinHeap; 8 struct HeapStruct { 9 int *Data; /* 存储元素数组 */ 10 int Size; /* 堆中当前元素个数 */ 11 int Capacity; /* 堆的最大容量 */ 12 }; 13 14 MinHeap CreateHeap(int MaxSize) 15 { 16 MinHeap H = (MinHeap)malloc(sizeof(struct HeapStruct)); 17 H->Data = (int *)malloc((MaxSize+1)*sizeof(int)); 18 H->Size = 0; 19 H->Capacity = MaxSize; 20 H->Data[0] = MINDATA; /* 定义"哨兵"为大于堆中所有可能元素的值*/ 21 22 return H; 23 } 24 25 int IsFull(MinHeap H) 26 { 27 return (H->Size == H->Capacity); 28 } 29 30 int Insert(MinHeap H, int X) 31 { 32 int i; 33 if(IsFull(H)) { 34 printf("MinHeap full"); 35 return ERROR; 36 } 37 i = ++H->Size; /* i指向插入后堆中的最后一个元素的位置 */ 38 for(;H->Data[i/2]>X;i=i/2) 39 H->Data[i] = H->Data[i/2]; /* 上滤X */ 40 H->Data[i] = X; /* 将X插入 */ 41 return 0; 42 } 43 44 int IsEmpty(MinHeap H) 45 { 46 return (H->Size == 0); 47 } 48 49 int DeleteMin(MinHeap H) 50 { 51 int Parent, Child; 52 int MinItem, X; 53 54 if(IsEmpty(H)) { 55 printf("MinHeap Empty"); 56 return ERROR; 57 } 58 59 MinItem = H->Data[1]; //第一个元素 60 X = H->Data[H->Size--]; //最后一个元素 61 for(Parent=1;Parent*2<=H->Size;Parent=Child) { 62 Child = Parent*2; 63 if((Child!=H->Size)&&(H->Data[Child]>H->Data[Child+1])) 64 Child++; 65 if(X <= H->Data[Child]) break; 66 else 67 H->Data[Parent] = H->Data[Child]; 68 } 69 H->Data[Parent] = X; 70 71 return MinItem; 72 } 73 74 void PercDown(MinHeap H, int p) 75 { 76 int Parent, Child; 77 int X; 78 79 X = H->Data[p]; /* 取出根结点存放的值 */ 80 for(Parent=p;Parent*2<=H->Size;Parent=Child) { 81 Child = Parent * 2; 82 if((Child!=H->Size)&&(H->Data[Child]>H->Data[Child+1])) 83 Child++; /* Child指向左右子结点的较小者 */ 84 if(X <= H->Data[Child]) break; /* 找到了合适位置 */ 85 else /* 下滤X */ 86 H->Data[Parent] = H->Data[Child]; 87 } 88 H->Data[Parent] = X; 89 } 90 91 void BuildHeap(MinHeap H) 92 { 93 int i; 94 /* 从最后一个结点的父节点开始,到根结点1 */ 95 for(i=H->Size/2;i>0;i--) 96 PercDown(H, i); 97 } 98 99 void Print(MinHeap H) 100 { 101 int i; 102 printf("H: "); 103 for(i=1;i<H->Size;i++) 104 printf("%d ", H->Data[i]); 105 printf(" "); 106 }