优先队列 c语言,最小堆 / 优先队列(C语言实现)

最近找实习,复习下数据结构方面的内容。

完全二叉树有两种形态,一种是:二叉树的所有子树要么没有孩子,要么一定有左孩子。另一种是:二叉树要么没有子树,要么一定左右子树都有。

堆是一种经过排序的完全二叉树,其中任一非终端节点的数据值均不大于(或不小于)其左孩子和右孩子节点的值。

最大堆和最小堆是二叉堆的两种形式。

最大堆:根结点的键值是所有堆结点键值中最大者。

最小堆:根结点的键值是所有堆结点键值中最小者。

在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。优先队列具有最高进先出 (largest-in,first-out)的行为特征。优先队列可以用堆来实现。

下面我们用数组来实现一个最小堆。

代码如下:

MinHeap.h

#ifndef DataStructures_MinHeap_h#define DataStructures_MinHeap_hstruct MinHeap;

typedef struct MinHeap * MinPriorityQueue;

typedef int ElementType;

// 初始化堆MinPriorityQueue initialize(int maxElements);

// 销毁堆void destroy(MinPriorityQueue pqueue);

// 清空堆中的元素void makeEmpty(MinPriorityQueue pqueue);

// 插入操作void insert(ElementType x, MinPriorityQueue pqueue);

// 删除最小者操作,返回被删除的堆顶元素ElementType deleteMin(MinPriorityQueue pqueue);

// 查找最小者(堆顶)ElementType findMin(MinPriorityQueue pqueue);

// 判断堆是否为空int isEmpty(MinPriorityQueue pqueue);

// 判断堆是否满int isFull(MinPriorityQueue pqueue);

// 通过一个数组来建堆,相当于将用数组实现的无序树转换为堆序树MinPriorityQueue buildHeap_insert(int *arr, int n);

MinPriorityQueue buildHeap_percolate(int *arr, int n);

// 打印堆void printMinPriorityQueue(MinPriorityQueue pqueue);

#endif

MinHeap.c

#include #include #include "MinHeap.h"/* 标记节点,类似于链表中的表头节点 * 该值必须小于所有最小堆中的元素,设其值为-1 */#define SentinelElement -1/* * 使用数组实现堆 * * capacity 数组的最大容量 * size 数组的长度 * elements 堆中的元素存放的数组 */struct MinHeap{

int capacity;

int size;

ElementType *elements;

// 堆的元素个数为size,实际上用来存储的数组的长度为size + 1,还包括一个sentinel元素}

;

voidPQueueNULLWarning(){

printf("Warning: Minimum Priority Queue is NULL");

}

voidoutOfSpaceFatalError(){

printf("Fatal Error: Out of space");

abort();

}

MinPriorityQueueinitialize(int maxElements){

MinPriorityQueue pqueue;

if (maxElements <= 0) {

printf("Fail to initialize: maxElements <= 0");

return NULL;

}

pqueue = malloc(sizeof(struct MinHeap));

if (pqueue == NULL) outOfSpaceFatalError();

// 数组的第0个元素是个sentinel标记节点,计入数组容量中,但不计入capcaity或size中 pqueue->size = 0;

pqueue->capacity = maxElements;

pqueue->elements = malloc(sizeof(ElementType) * (pqueue->capacity + 1));

if (pqueue->elements == NULL) outOfSpaceFatalError();

else pqueue->elements[0] = SentinelElement;

return pqueue;

}

voiddestroy(MinPriorityQueue pqueue){

if (pqueue != NULL) {

// 在GNU99标准中,free(NULL)什么都不做直接返回,所以不用判断pqueue->elements是否为NULL free(pqueue->elements);

free(pqueue);

}

}

voidmakeEmpty(MinPriorityQueue pqueue){

if (pqueue != NULL) pqueue->size = 0;

else PQueueNULLWarning();

}

/* * 插入时,堆中的元素执行下滤操作 * 删除时,堆中的元素执行上滤操作 *//* * 插入的时间复杂度为O(log N),N为最小堆中的元素个数 * 实际上,其平均执行时间为O(1) */voidinsert(ElementType x, MinPriorityQueue pqueue){

if (pqueue == NULL) PQueueNULLWarning();

if (isFull(pqueue)) {

printf("Fail to insert: Priority Queue is Full");

return;

}

else {

int i;

// sentinel element在这里作为elements[0]被比较,是循环的终止条件 for (i = ++pqueue->size;

x < pqueue->elements[i / 2];

i /= 2) pqueue->elements[i] = pqueue->elements[i / 2];

// 下滤操作 pqueue->elements[i] = x;

}

}

/* * 删除操作的平均时间为O(log N) */ElementTypedeleteMin(MinPriorityQueue pqueue){

if (pqueue == NULL) {

PQueueNULLWarning();

return SentinelElement;

}

if (isEmpty(pqueue)) {

printf("Fail to delete: Priority Queue is Empty");

return SentinelElement;

}

int i, child;

ElementType minElement, lastElement;

// 注意对某个节点进行上滤操作时,要判断该节点是有两个儿子还是一个儿子 minElement = pqueue->elements[1];

lastElement = pqueue->elements[pqueue->size--];

for (i = 1;

i * 2 <= pqueue->size;

i = child) {

child = i * 2;

// 节点i只有一个儿子时必有i * 2 = pqueue->size if (child < pqueue->size && pqueue->elements[child] > pqueue->elements[child + 1]) child++;

if (lastElement < pqueue->elements[child]) break;

else pqueue->elements[i] = pqueue->elements[child];

// 上滤操作 }

pqueue->elements[i] = lastElement;

return minElement;

// 返回被删除的元素}

/* * 执行时间:O(1) */ElementTypefindMin(MinPriorityQueue pqueue){

if (pqueue == NULL) {

PQueueNULLWarning();

return SentinelElement;

}

else return pqueue->elements[1];

}

intisEmpty(MinPriorityQueue pqueue){

if (pqueue == NULL) {

PQueueNULLWarning();

return -1;

}

else return (pqueue->size == 0);

}

intisFull(MinPriorityQueue pqueue){

if (pqueue == NULL) {

PQueueNULLWarning();

return -1;

}

else return (pqueue->size == pqueue->capacity);

}

voidpercolateDown(int *arr, int len, int i){

int n = len - 1;

int tmp;

if (i * 2 == n && arr[i] > arr[n]) // 只有左儿子的节点,并且左儿子比本节点的值要小,交换 {

tmp = arr[i];

arr[i] = arr[n];

arr[n] = tmp;

}

else // 有两个儿子的节点 {

if (arr[i * 2] > arr[i * 2 + 1]) // 右儿子较小 {

if (arr[i] > arr[i * 2 + 1]) // 如果本节点比右儿子大,交换 {

tmp = arr[i];

arr[i] = arr[i * 2 + 1];

arr[i * 2 + 1] = tmp;

}

}

else // 左儿子较小 {

if (arr[i] > arr[i * 2]) // 如果本节点比左儿子大,交换 {

tmp = arr[i];

arr[i] = arr[i * 2];

arr[i * 2] = tmp;

}

}

}

}

MinPriorityQueuebuildHeap_percolate(int *arr, int n){

if (arr == NULL) {

printf("Error: Array is NULL");

return NULL;

}

MinPriorityQueue pqueue;

pqueue = malloc(sizeof(struct MinHeap));

if (pqueue == NULL) outOfSpaceFatalError();

ElementType *elements = malloc(sizeof(ElementType) * (n + 1));

if (elements == NULL) outOfSpaceFatalError();

int i;

for (i = 1;

i <= n;

i++) elements[i] = arr[i - 1];

elements[0] = SentinelElement;

for (i = n / 2;

i > 0;

i--) percolateDown(elements, n + 1, i);

pqueue->elements = elements;

pqueue->size = n;

pqueue->capacity = n * 2;

return pqueue;

}

/* * 通过n次插入元素建立堆,由于每次插入的平均执行时间为O(1),所以建堆平均时间为O(N) */MinPriorityQueuebuildHeap_insert(int *arr, int n){

MinPriorityQueue pqueue;

if (arr == NULL) {

printf("Array is NULL, fail to build heap");

return NULL;

}

pqueue = initialize(n * 2);

for (int i = 0;

i < n;

i++) insert(arr[i], pqueue);

return pqueue;

}

voidprintMinPriorityQueue(MinPriorityQueue pqueue){

if (pqueue == NULL) {

PQueueNULLWarning();

return;

}

if (pqueue->elements == NULL) {

printf("Fail to print: Elements of priority queue is NULL");

return;

}

if (isEmpty(pqueue)) {

printf("Empty Prioirty Queuen");

return;

}

printf("Priority Queuen");

for (int i = 1;

i <= pqueue->size;

i++) printf("Element[%d] = %dn", i, pqueue->elements[i]);

printf("n");

}

建堆的测试代码:

#include #include #include "MinHeap.h"int main(int argc, const char * argv[]){

int a[5] = {

5, 4, 3, 2, 1}

;

MinPriorityQueue pqueue_ins = buildHeap_insert(a, 5);

MinPriorityQueue pqueue_per = buildHeap_percolate(a, 5);

printMinPriorityQueue(pqueue_ins);

printMinPriorityQueue(pqueue_per);

return 0;

}

分别使用插入和下滤两种方式建堆,所以建立的结果是不同的,输出如下:

Priority QueueElement[1] = 1Element[2] = 2Element[3] = 4Element[4] = 5Element[5] = 3Priority QueueElement[1] = 1Element[2] = 5Element[3] = 3Element[4] = 2Element[5] = 4

最大堆实现类似。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值