堆是一种特殊的“队列”,它取出元素的顺序是依照元素的优先级大小,而不是元素进入队列的先后顺序。堆具有两个特性,1.结构性:它是能用数组表示的完全二叉树。2.堆序性:任一结点的关键字是其子树所有结点的最大值(最大堆)或最小值(最小堆),即任意子树也应该是个堆。
根据最小堆的结构特性,本文使用含有哨兵元素的数组实现了最小堆的创建、插入和删除。
#include
#include
#define MinData -100//哨兵元素的值
typedef struct HeapStruct{
int *p;
int size;
int capacity;
} *MinHeap;
MinHeap Init_MinHeap(int MaxSize);
int IsFull(MinHeap H);
int IsEmpty(MinHeap H);
void Insert(MinHeap H, int data);
int Delete(MinHeap H);
void BuildMinHeap(MinHeap H, int N);
void PrintValue(MinHeap H);
int IsFull(MinHeap H)
{
return (H->size == H->capacity) ? 1 : 0;
}
int IsEmpty(MinHeap H)
{
return (H->size == 0) ? 1 : 0;
}
void PrintValue(MinHeap H)
{
int i;
printf("最小堆中的元素依次为:");
for (i = 1; i <= H->size; i++)
printf("%d ", H->p[i]);
printf("\n");
}
该函数给最小堆分配了内存空间并完成初始化操作,此时最小堆中元素为空。
MinHeap Init_MinHeap(int MaxSize)
{
MinHeap H = (MinHeap)malloc(sizeof(HeapStruct));
H->p = (int*)malloc((MaxSize + 1) * sizeof(int));
H->p[0] = MinData;
H->size = 0;
H->capacity = MaxSize;
return H;
}
插入元素的关键是把要插入的元素放在最小堆的合适位置中。所谓合适即不破坏最小堆的结构性和堆序性。
为了将一个元素data插入到堆中,在遵守堆的结构性特点(完全二叉树)基础上,在下一个空闲位置创建一个空穴。如果元素data可以放在该空穴中而并不破坏堆的序,则完成插入。否则,把空穴的父节点上的元素移入该空穴中,这样,空穴就朝着根的方向上移动一层。继续该过程知道元素data能放入空穴中。因为空穴在逐渐往树的上方移动,所以把这种策略成为“上滤”。
void Insert(MinHeap H, int data)
{
int i;
if (IsFull(H))
{
printf("最小堆已满,无法插入元素");
return;
}
for (i = ++H->size; data < H->p[i / 2]; i /= 2)
H->p[i] = H->p[i / 2];
H->p[i] = data;
}
删除元素的关键是把堆中最后一个元素移动到最小堆中的合适位置。所谓合适即不破坏最小堆的结构性和堆序性。
删除元素以类似于插入的方式处理。找到最小元很容易(第一个元素),困难的部分是删除后的调整。当删除一个最小元时,在根节点处产生一个空穴。由于现在堆里少了一个元素,因为堆中最后一个元素lastvalue必须移动到该堆的某个地方。通常我们将空穴的两个儿子中较小者移入空穴,这样就把空穴向下退了一层。重复该步骤知道lastvalue可以放在空穴中。通常把这种策略成为“下滤”。
int Delete(MinHeap H)
{
int minvalue , lastvalue, child, parent;
if (IsEmpty(H))
{
printf("最小堆已满,无法删除元素");
return -999;
}
minvalue = H->p[1];
lastvalue = H->p[H->size--];
for (parent = 1; 2 * parent <= H->size; parent = child)
{
child = 2 * parent;/*默认左结点的元素值更小*/
if (child != H->size && H->p[child + 1] < H->p[child])/*若右节点的元素值更小,则调整child*/
child++;
if (lastvalue < H->p[child])
break;
else
H->p[parent] = H->p[child];
}
H->p[parent] = lastvalue;
return minvalue;
}
在创建最小堆时,可以 : 通过插入操作Insert函数,将N 个元素一个个相继插入到一个初始为空的堆中去 ,但其时间代价最大为O(NlogN)。
也可以在线性复杂度下建立最小堆,基本步骤为:1. 将N个元素按输入顺序存入,先满足完全二叉树的结构特性。2.调整各节点位置,以满足最大堆的堆序性。这样创建最小堆时的时间代价为O(N)。在调整节点位置时,只需对从第N/2个节点到第一个节点依次应用“下滤”策略即可,其实也就是进行了N/2次的近似删除操作。
void BuildMinHeap(MinHeap H, int N)
{
int i, num, parent, child, root, lastvalue;
if (N > H->capacity)
{
printf("要创建的元素个数超过堆的最大容量,创建失败");
return;
}
for (i = 1; i <= N; i++)
{
printf("请输入要插入的元素值:");
scanf_s("%d", &num);
H->p[i] = num;
}
H->size = N;
root = N / 2;/*从第N/2个结点到第1个结点依次进行下滤 近似N/2次删除操作*/
while (root)
{
lastvalue = H->p[root];
for (parent = root; 2 * parent <= H->size; parent = child)
{
child = 2 * parent;/*默认左结点的元素值更小*/
if (child != H->size && H->p[child + 1] < H->p[child])/*右结点元素值更小*/
child++;
if (lastvalue < H->p[child])
break;
else
H->p[parent] = H->p[child];
}
H->p[parent] = lastvalue;
--root;
}
}
测试序列:150 80 40 30 10 70 110 100 20 90 60 50 120 140 130
正确结果为:10 20 40 30 60 50 110 100 150 90 80 70 120 140 130
void main()
{
int num;
MinHeap H;
H = Init_MinHeap(100);
BuildMinHeap(H, 15);
PrintValue(H);
printf("请输入你要插入的数据:");
scanf_s("%d", &num);
Insert(H, num);
PrintValue(H);
printf("请输入你要插入的数据:");
scanf_s("%d", &num);
Insert(H, num);
PrintValue(H);
num = Delete(H);
printf("删除的元素为:%d\n", num);
PrintValue(H);
}