30008:整数小根堆
题目
实现不透明的整数小根堆,支持插入、查看最小值和弹出最小值。
解析
数组下标 i 的孩子是 2*i+1 和 2*i+2。插入先确保有容量,再把新值向上移动;弹出先保存根值,把最后一个元素移到根,再向下移动。两种移动都只交换违反堆序的父子,因此不变式可以局部恢复。
答案
c
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
typedef struct IntMinHeap IntMinHeap;
struct IntMinHeap {
int64_t *items;
size_t size;
size_t capacity;
};
IntMinHeap *int_min_heap_create(void) {
IntMinHeap *heap = malloc(sizeof *heap);
if (heap == NULL) {
return NULL;
}
heap->items = NULL;
heap->size = 0;
heap->capacity = 0;
return heap;
}
void int_min_heap_destroy(IntMinHeap *heap) {
if (heap == NULL) {
return;
}
free(heap->items);
free(heap);
}
static bool heap_reserve(IntMinHeap *heap) {
size_t maximum = SIZE_MAX / sizeof *heap->items;
if (heap->size < heap->capacity) {
return true;
}
if (heap->capacity >= maximum) {
return false;
}
size_t next = heap->capacity == 0 ? 8 : heap->capacity;
if (next > maximum) {
return false;
}
while (next <= heap->size) {
if (next > maximum / 2) {
next = maximum;
break;
}
next *= 2;
}
if (next <= heap->size) {
return false;
}
int64_t *replacement = realloc(
heap->items, next * sizeof *replacement
);
if (replacement == NULL) {
return false;
}
heap->items = replacement;
heap->capacity = next;
return true;
}
static void swap_i64(int64_t *left, int64_t *right) {
int64_t temporary = *left;
*left = *right;
*right = temporary;
}
static void sift_up(IntMinHeap *heap, size_t index) {
while (index > 0) {
size_t parent = (index - 1) / 2;
if (heap->items[parent] <= heap->items[index]) {
break;
}
swap_i64(&heap->items[parent], &heap->items[index]);
index = parent;
}
}
static void sift_down(IntMinHeap *heap, size_t index) {
for (;;) {
size_t smallest = index;
if (index < heap->size / 2) {
size_t left = index * 2 + 1;
size_t right = left + 1;
if (heap->items[left] < heap->items[smallest]) {
smallest = left;
}
if (right < heap->size &&
heap->items[right] < heap->items[smallest]) {
smallest = right;
}
}
if (smallest == index) {
return;
}
swap_i64(&heap->items[index], &heap->items[smallest]);
index = smallest;
}
}
bool int_min_heap_push(IntMinHeap *heap, int64_t value) {
if (heap == NULL || !heap_reserve(heap)) {
return false;
}
heap->items[heap->size] = value;
++heap->size;
sift_up(heap, heap->size - 1);
return true;
}
bool int_min_heap_peek(
const IntMinHeap *heap,
int64_t *out_value
) {
if (heap == NULL || out_value == NULL || heap->size == 0) {
return false;
}
*out_value = heap->items[0];
return true;
}
bool int_min_heap_pop(
IntMinHeap *heap,
int64_t *out_value
) {
if (heap == NULL || out_value == NULL || heap->size == 0) {
return false;
}
int64_t result = heap->items[0];
--heap->size;
if (heap->size > 0) {
heap->items[0] = heap->items[heap->size];
sift_down(heap, 0);
}
*out_value = result;
return true;
}
size_t int_min_heap_size(const IntMinHeap *heap) {
return heap == NULL ? 0 : heap->size;
}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
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
push 和 pop 的时间复杂度为 peek 为