最小堆 / 优先队列(C语言实现)
来源:互联网 发布:淘宝无线端链接生成 编辑:程序博客网 时间:2024/05/01 19:25
最近找实习,复习下数据结构方面的内容。
完全二叉树有两种形态,一种是:二叉树的所有子树要么没有孩子,要么一定有左孩子。另一种是:二叉树要么没有子树,要么一定左右子树都有。
堆是一种经过排序的完全二叉树,其中任一非终端节点的数据值均不大于(或不小于)其左孩子和右孩子节点的值。
最大堆和最小堆是二叉堆的两种形式。
最大堆:根结点的键值是所有堆结点键值中最大者。
最小堆:根结点的键值是所有堆结点键值中最小者。
在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。优先队列具有最高进先出 (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 <stdio.h>#include <stdlib.h>#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 child; int n = len - 1; ElementType tmp; for (tmp = arr[i]; i * 2 < n; i = child) { child = i * 2; if (child < n && arr[child] > arr[child + 1]) child++; if (tmp > arr[child]) arr[i] = arr[child]; else break; } arr[i] = 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 Queue\n"); return; } printf("Priority Queue\n"); for (int i = 1; i <= pqueue->size; i++) printf("Element[%d] = %d\n", i, pqueue->elements[i]); printf("\n");}
建堆的测试代码:
voidbuildHeapTest(){ int a[9] = {9, 8, 7, 6, 5, 4, 3, 2, 1}; MinPriorityQueue pqueue_ins = buildHeap_insert(a, 9); MinPriorityQueue pqueue_per = buildHeap_percolate(a, 9); printMinPriorityQueue(pqueue_ins); printMinPriorityQueue(pqueue_per);}int main(int argc, const char * argv[]){ buildHeapTest(); return 0;}
分别使用插入和下滤两种方式建堆,所以建立的结果是不同的,输出如下:
Priority QueueElement[1] = 1Element[2] = 2Element[3] = 4Element[4] = 3Element[5] = 7Element[6] = 8Element[7] = 5Element[8] = 9Element[9] = 6Priority QueueElement[1] = 1Element[2] = 2Element[3] = 3Element[4] = 6Element[5] = 5Element[6] = 4Element[7] = 7Element[8] = 8Element[9] = 9
最大堆实现类似。
下面用最小堆解决“在数组中寻找第k大元素”的问题。
测试代码如下:
voidfindKMaxElementTest(int k){ int a[10] = {20, 10, 11, 21, 36, 90, 100, 200, 25, 9}; MinPriorityQueue pqueue = buildHeap_insert(a, 10); for (int i = 1; i < k; i++) deleteMin(pqueue); printf("第%d大元素为:%d\n", k, findMin(pqueue));}int main(int argc, const char * argv[]){ findKMaxElementTest(5); return 0;}
由于建堆最多需要花费O(N)的时间,每次删除操作最多需要花费O(log N)的时间,所以该算法的时间复杂度为O(N + klog N),如果k = [N / 2],那么时间复杂度为Θ(N log N)
主要参考了Mark Allen Weiss的《数据结构与算法分析 —— C语言描述》。
0 0
- 最小堆 / 优先队列(C语言实现)
- c语言最小堆的实现-优先队列
- 优先队列二叉堆 C语言实现
- 最小优先队列--堆实现
- 最小对/优先队列(C语言实现)
- 最小堆实现优先队列解决修理牧场(c/c++)
- 数据结构实现之最小优先队列(最小堆)
- 优先队列(最小堆)
- 优先队列(堆) - C语言实现(摘自数据结构与算法分析 C语言描述)
- 优先队列(堆) - C语言实现(摘自数据结构与算法分析 C语言描述)
- 优先队列(堆) - C语言实现(摘自数据结构与算法分析 C语言描述)
- 最小堆优先队列C++实现
- 用最小堆实现优先队列
- 简单优先队列实现-基于最小堆
- 基于最大堆的最大优先队列的实现(C语言)
- POJ 2051(最小堆/优先队列)
- 优先队列C语言实现
- 优先队列--C语言实现
- 关于博主
- 第六周作业1——利用哈夫曼编码英文字母表
- 线程pthread_create,pthread_join
- poj 2100 尺取法
- 实现输出h264直播流的rtmp服务器 flash直播服务器
- 最小堆 / 优先队列(C语言实现)
- 10gocm->session1->数据库和网络配置
- HIT_Training_20140415
- myeclipse db brower 链接 sql server 2008 时
- VS2012的插件,可以编辑lua,高亮,自动完成等。BabeLua is a free lua editor/debuger (vs extension)
- 序列号
- Java正则表达式例子汇总
- java中的变量和方法
- UVA 537 Artificial Intelligence?