最小对/优先队列(C语言实现)

来源:互联网 发布:真维斯淘宝 编辑:程序博客网 时间:2024/05/22 12:45
最小堆 / 优先队列(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(inti = 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(inti = 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


最大堆实现类似。



0 1
原创粉丝点击