最小堆 / 优先队列(C语言实现)
转自网址:http://www.2cto.com/kf/201404/293694.html
2014-04-17     0个评论   来源:最小堆 / 优先队列(C语言实现)  
收藏  我要投稿

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

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

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

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

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

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

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

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

代码如下:

MinHeap.h

?
#ifndef DataStructures_MinHeap_h
#define DataStructures_MinHeap_h
struct MinHeap;
typedef struct MinHeap * MinPriorityQueue;
typedef intElementType;
// 初始化堆
MinPriorityQueue initialize(intmaxElements);
// 销毁堆
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
#include <stdio.h>
#include <stdlib.h>
#include "MinHeap.h"
/* 标记节点,类似于链表中的表头节点
 * 该值必须小于所有最小堆中的元素,设其值为-1
 */
#define SentinelElement -1
/*
 * 使用数组实现堆
 *
 * capacity 数组的最大容量
 * size     数组的长度
 * elements 堆中的元素存放的数组
 */
struct MinHeap
{
    intcapacity;
    intsize;
    ElementType *elements;// 堆的元素个数为size,实际上用来存储的数组的长度为size + 1,还包括一个sentinel元素
};
void
PQueueNULLWarning()
{
    printf("Warning: Minimum Priority Queue is NULL");
}
void
outOfSpaceFatalError()
{
    printf("Fatal Error: Out of space");
    abort();
}
MinPriorityQueue
initialize(intmaxElements)
{
    MinPriorityQueue pqueue;
     
    if(maxElements <= 0)
    {
        printf("Fail to initialize: maxElements <= 0");
        returnNULL;
    }
     
    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;
     
    returnpqueue;
}
void
destroy(MinPriorityQueue pqueue)
{
    if(pqueue != NULL)
    {
        // 在GNU99标准中,free(NULL)什么都不做直接返回,所以不用判断pqueue->elements是否为NULL
        free(pqueue->elements);
        free(pqueue);
    }
}
void
makeEmpty(MinPriorityQueue pqueue)
{
    if(pqueue != NULL)
        pqueue->size =0;
    else
        PQueueNULLWarning();
}
/*
 * 插入时,堆中的元素执行下滤操作
 * 删除时,堆中的元素执行上滤操作
 */
/*
 * 插入的时间复杂度为O(log N),N为最小堆中的元素个数
 * 实际上,其平均执行时间为O(1)
 */
void
insert(ElementType x, MinPriorityQueue pqueue)
{
    if(pqueue == NULL)
        PQueueNULLWarning();
     
    if(isFull(pqueue))
    {
        printf("Fail to insert: Priority Queue is Full");
        return;
    }
    else
    {
        inti;
         
        // 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)
 */
ElementType
deleteMin(MinPriorityQueue pqueue)
{
    if(pqueue == NULL)
    {
        PQueueNULLWarning();
        returnSentinelElement;
    }
     
    if(isEmpty(pqueue))
    {
        printf("Fail to delete: Priority Queue is Empty");
        returnSentinelElement;
    }
     
    inti, 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;
     
    returnminElement; // 返回被删除的元素
}
/*
 * 执行时间:O(1)
 */
ElementType
findMin(MinPriorityQueue pqueue)
{
    if(pqueue == NULL)
    {
        PQueueNULLWarning();
        returnSentinelElement;
    }
    else
        returnpqueue->elements[1];
}
int
isEmpty(MinPriorityQueue pqueue)
{
    if(pqueue == NULL)
    {
        PQueueNULLWarning();
        return-1;
    }
    else
        return(pqueue->size == 0);
}
int
isFull(MinPriorityQueue pqueue)
{
    if(pqueue == NULL)
    {
        PQueueNULLWarning();
        return-1;
    }
    else
        return(pqueue->size == pqueue->capacity);
}
void
percolateDown(int*arr, int len, int i)
{
    intn = len - 1;
    inttmp;
    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;
            }
        }
    }
}
MinPriorityQueue
buildHeap_percolate(int*arr, int n)
{
    if(arr == NULL)
    {
        printf("Error: Array is NULL");
        returnNULL;
    }
     
    MinPriorityQueue pqueue;
    pqueue = malloc(sizeof(struct MinHeap));
     
    if(pqueue == NULL)
        outOfSpaceFatalError();
    ElementType *elements = malloc(sizeof(ElementType) * (n +1));
    if(elements == NULL)
        outOfSpaceFatalError();
     
    inti;
    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;
     
    returnpqueue;
}
/*
 * 通过n次插入元素建立堆,由于每次插入的平均执行时间为O(1),所以建堆平均时间为O(N)
 */
MinPriorityQueue
buildHeap_insert(int*arr, int n)
{
    MinPriorityQueue pqueue;
     
    if(arr == NULL)
    {
        printf("Array is NULL, fail to build heap");
        returnNULL;
    }
     
    pqueue = initialize(n *2);
    for(int i = 0; i < n; i++)
        insert(arr[i], pqueue);
     
    returnpqueue;
}
void
printMinPriorityQueue(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");
}</stdlib.h></stdio.h>

建堆的测试代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
#include <stdlib.h>
#include "MinHeap.h"
int main(int argc, const char * argv[])
{
    inta[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);
     
    return0;
}</stdlib.h></stdio.h>

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
Priority Queue
Element[1] =1
Element[2] =2
Element[3] =4
Element[4] =5
Element[5] =3
Priority Queue
Element[1] =1
Element[2] =5
Element[3] =3
Element[4] =2
Element[5] =4

最大堆实现类似。

最小对/优先队列(C语言实现)相关推荐

  1. 数据结构之优先队列:最小索引优先队列,Python代码实现——15

    最小索引优先队列(Min index priority queue) 在之前实现的最大优先队列和最小优先队列,他们可以分别快速访问到队列中最大元索和最小元素,但是他们有一 个缺点,就是没有办法通过索引 ...

  2. leetcode 703. 数据流中的第K大元素 最小堆解法 c语言

    如题: 设计一个找到数据流中第K大元素的类(class).注意是排序后的第K大元素,不是第K个不同的元素. 你的 KthLargest 类需要一个同时接收整数 k 和整数数组nums 的构造器,它包含 ...

  3. LeetCode 1962. 移除石子使总数最小(优先队列)

    文章目录 1. 题目 2. 解题 1. 题目 给你一个整数数组 piles ,数组 下标从 0 开始 ,其中 piles[i] 表示第 i 堆石子中的石子数量. 另给你一个整数 k ,请你执行下述操作 ...

  4. 流网络的最小割问题c语言,「网络流24题」最小路径覆盖问题

    Description 问题描述: 给定有向图G=(V,E).设P 是G 的一个简单路(顶点不相交)的集合.如果V 中每个顶点恰好在P 的一条路上,则称P是G 的一个路径覆盖.P 中路径可以从V 的任 ...

  5. ieee754最小规格化正数C语言,IEEE754标准浮点格式

    两种基本浮点格式:单精度和双精度.IEEE单精度格式具有24位有效数字,并总共占用32 位.IEEE双精度格式具有53位有效数字精度,并总共占用64位 两种扩展浮点格式:单精度扩展和双精度扩展.此标准 ...

  6. 找出矩阵在行中最大、列中最小的元素 C语言

    函数fun的功能是:在3x4的矩阵中找出在行上最大.在列上最小的那个元素,若没有符合条件的元素则输出相应信息. 例如有下列矩阵: 1  2 13  4 7  8 10  6 3  5  9  7 程序 ...

  7. 华为OD机试 - 数组组成的最小数字(C 语言解题)【独家】

    最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单 华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典 [华为OD机试]全流程解析+经验分享,题型分 ...

  8. 流网络的最小割问题c语言,网络流基础-最大流最小割定理

    最大流最小割定理,指网络流的最大流等于其最小割. 最大流指符合三个性质的前提下,从S到T能流过的最大流量. 最小割指符合割的定义,最小的割容量. 求最大流: 不断寻找增广路,计算能增加的最小流量,然后 ...

  9. 数据结构——最大堆和最小堆(C语言)

    定义: 最大堆和最小堆都是一棵完全二叉树. 最大堆:是指根节点的关键字值是堆中的最大关键字值,且每个节点若有儿子节点,其关键字值都不小于其儿子节点的关键字值. 最小堆:是指根节点的关键字值是堆中的最小 ...

  10. 矩阵连乘最小计算次数 C语言

    先上代码 #include <stdio.h> #include <algorithm> #include <string.h> #include <cmat ...

最新文章

  1. python rfind函数用法_Python语法速查:字符串格式简单处理、子串查找与判断方法?...
  2. springcloud 入门 10 (eureka高可用)
  3. Leetcode--130. 被围绕的区域(java)
  4. (vue基础试炼_04)使用组件改造TodoList
  5. python算法系列资料集(一)-2022.03.15
  6. 西安python后端招聘_有大佬招 Python 后端初级人员吗?
  7. serialize和unserialize函数
  8. 用ABAP编程破解世界上最难数独游戏
  9. html播放flv直播源,http-flv 直播
  10. python模拟支付宝扫码登录_Python接入支付宝进行PC端支付
  11. 【BZOJ4198】【NOI2015】荷马史诗(贪心,Huffman树)
  12. kali获取同局域网设备的图片信息
  13. 安装ie9提示未能完成安装_win10系统安装iE提示“internet Explorer未能完成安装”的方法介绍...
  14. 《深入理解计算机系统》——低谷中的重新振作
  15. Pro Android学习笔记(一五五):传感器(5): 磁场传感器和方位(上)
  16. python均线斜率_【每日一策】Matlab量化交易策略之 均线拐头配合出场
  17. 弘辽科技:拼多多关键词怎么添加?店铺没转化咋办?
  18. aseprite手机版_texture packs泰拉瑞亚
  19. [luoguP2862] [USACO06JAN]把牛Corral the Cows(二分 + 乱搞)
  20. Go 语言 exec 实时获取外部命令的执行输出

热门文章

  1. Go Node.js 生成的exe公布成windows服务
  2. 25 The Go image/draw package go图片/描绘包:图片/描绘包的基本原理
  3. TortoiseSVN使用指南
  4. unity 多选枚举
  5. 【Python】django安装
  6. smpt authentification 配置
  7. win10下安装Mysql5.7
  8. Sql Server 中 根据具体的值 查找该值所在的表和字段
  9. 你不可不知道的CSS水平+垂直居中方案大全
  10. 15c语言语句_如何学好C语言判断语句?攻略if语句是第一步