优先队列(堆)——二叉堆的实现

一般数据结构中的堆指的是二叉堆(Binary heap)。堆是一棵完全二叉树,可用数组来表示。对于数组中任意位置i上的元素,其左儿子在位置2i上,右儿子在左儿子后的单元(2i+1)中,它的父亲在位置[i/2](整数除法)上。

在堆实现过程中下表为0的元素作为标记(这个值必须小于其中的任何一个值),用标记只是为了减少判断次数。

fatal.h定义错误处理

#include 
#include 

#define Error(Str) fprintf(stderr, "%s\n", Str),exit(1)
#define FatalError(Str) Error(Str)

binheap.h函数声明

typedef int ElementType;

#ifndef BINHEAP_H
#define BINHEAP_H

typedef unsigned int Position;
struct HeapStruct
{
	int Capacity;
	int Size;
	ElementType *Elements;
};

typedef struct HeapStruct *PriorityQueue;

PriorityQueue Initialize(int MaxElements);//初始化堆
void Destory(PriorityQueue H);//销毁堆
int IsFull(PriorityQueue H);//是否已满
int IsEmpty(PriorityQueue H);//是否为空
void MakeEmpty(PriorityQueue H);//清空堆
void Insert(ElementType X, PriorityQueue H);//插入元素
ElementType DeleteMin(PriorityQueue H);//删除根
ElementType FindMin(PriorityQueue H);//查找最小值
void Delete(Position P, PriorityQueue H);//删除任意节点
void DecreaseKey(Position P, ElementType Delta, PriorityQueue H);//降低关键字值Delta
void IncreaseKey(Position P, ElementType Delta, PriorityQueue H);//增加关键字值Delta
PriorityQueue BuildHeap(ElementType A[], int N);//构建堆

#endif
binheap.c函数实现
#include "fatal.h"
#include "binheap.h"
#include 

#define MinPQSize (10)
#define MinData (-32767)

PriorityQueue Initialize(int MaxElements)
{
	PriorityQueue H;

	if(MaxElements < MinPQSize)
	{
		Error("Priority queue size too small");
		return NULL;
	}
	H = (PriorityQueue)malloc(sizeof(struct HeapStruct));
	if(H == NULL)
		FatalError("Out of space");
	H->Elements = (ElementType *)malloc(sizeof(ElementType) * (MaxElements + 1));//多申请一个空间用用作标记
	if(H->Elements == NULL)
		FatalError("Out of space");
	H->Capacity = MaxElements;
	H->Size = 0;
	H->Elements[0] = MinData;//0号元素作为标记
	return H;
}

void Destory(PriorityQueue H)
{
	free(H->Elements);
	free(H);
}

int IsFull(PriorityQueue H)
{
	return H->Size == H->Capacity;
}

int IsEmpty(PriorityQueue H)
{
	return H->Size == 0;
}

void MakeEmpty(PriorityQueue H)
{
	H->Size = 0;
}

void Insert(ElementType X, PriorityQueue H)
{
	int i;

	if(IsFull(H))
	{
		Error("Priority queue is full");
		return;
	}
	for(i = ++H->Size; H->Elements[i/2] > X; i /= 2)//采用上滤法进行插入,插入点到根路径上查找应该插入的位置
		H->Elements[i] = H->Elements[i/2];
	H->Elements[i] = X;
}

ElementType DeleteMin(PriorityQueue H)
{
	int i, child;
	ElementType MinElement, LastElement;
	if(IsEmpty(H))
	{
		Error("Priority queue is empty");
		return H->Elements[0];
	}
	MinElement = H->Elements[1];
	LastElement = H->Elements[H->Size--];
	for(i = 1; i * 2 <= H->Size; i = child)//采用下滤法。删除根节点后,将根的孩子节点中较小的一个上移,直到到达叶子节点,然后将最后一个元素的值复制给叶子节点。
	{
		child = i * 2;
		if(child != H->Size && H->Elements[child + 1] < H->Elements[child])
			child++;
		if(LastElement > H->Elements[child])
			H->Elements[i] = H->Elements[child];
		else
			break;
	}
	H->Elements[i] = LastElement;
	return MinElement;
}

ElementType FindMin(PriorityQueue H)
{
	if(IsEmpty(H))
	{
		Error("Priority queue is empty");
		return H->Elements[0];
	}
	return H->Elements[1];
}

static void PercolateDown(int n, PriorityQueue H)//下滤
{
	int i, child;
	ElementType Tmp;

	Tmp = H->Elements[n];
	for(i = n; i * 2 <= H->Size; i = child)
	{
		child = i * 2;
		if(child != H->Size && H->Elements[child + 1] < H->Elements[child])
			child++;
		if(Tmp > H->Elements[child])
			H->Elements[i] = H->Elements[child];
		else
			break;
	}
	H->Elements[i] = Tmp;
}

static void PercolateUp(int n, PriorityQueue H)//上滤
{
	int i;
	ElementType Tmp;

	Tmp = H->Elements[n];
	for(i = n; H->Elements[i] < H->Elements[i/2]; i /= 2)
		H->Elements[i] = H->Elements[i/2];
	H->Elements[i] = Tmp;
}

void Delete(Position P, PriorityQueue H)//降低关键字的值,使它小于根节点然后删除根节点即可完成该操作
{
	DecreaseKey(P, H->Elements[P], H);
	DeleteMin(H);
}

void DecreaseKey(Position P, ElementType Delta, PriorityQueue H)//降低关键字的值后采用上滤,将节点上调
{
	H->Elements[P] -= Delta;
	PercolateUp(P, H);
}

void IncreaseKey(Position P, ElementType Delta, PriorityQueue H)//增加关键字的值后采用下滤
{
	H->Elements[P] += Delta;
	PercolateDown(P, H);
}

PriorityQueue BuildHeap(ElementType A[], int N)
{
	PriorityQueue H;
	int i;

	H = Initialize(N);
	for(i = 1; i <= N; i++)
		H->Elements[i] = A[i-1];
	H->Size = H->Capacity;
	for(i = N / 2; i > 0; i--)
		PercolateDown(i, H);
	return H;
}
testmain.c测试
#include 
#include 
#include "binheap.h"

#define N 15

int main(void)
{
	PriorityQueue H;
	int arr[N] = {10,12,1,14,6,5,8,15,3,9,7,4,11,13,2};
	int i;

	H = Initialize(N);
	for(i = 0; i < N; i++)//插入法新建堆
		Insert(arr[i], H);
	for(i = 1; i <= N; i++)
		printf("%d ", H->Elements[i]);
	printf("\n");
	Delete(3, H);//删除跟的右孩子
	printf("after delete(%d):\n", 3);
	for(i = 1; i < N; i++)
		printf("%d ", H->Elements[i]);
	printf("\n");
	Destory(H);
	H = BuildHeap(arr, N);//新建完全二叉树,然后调整为堆,时间复杂度为O(N)
	for(i = 1; i <= N; i++)
		printf("%d ", H->Elements[i]);
	printf("\n");
	system("Pause");
	return 0;
}

运行结果分析

插入法新建堆,如下图所示:

优先队列(堆)——二叉堆的实现_第1张图片

线性时间构建堆,如下图所示:

优先队列(堆)——二叉堆的实现_第2张图片

程序运行结果,层序输出结果:

优先队列(堆)——二叉堆的实现_第3张图片

你可能感兴趣的:(数据结构)