├── .gitignore ├── LICENSE ├── Makefile ├── README.markdown ├── heap.c ├── heap.h └── main.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *~ 3 | *.swp 4 | *.swo 5 | test 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, Armon Dadgar 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the organization nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL ARMON DADGAR BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -Wall -std=c99 -O2 2 | DEPS=main.o heap.o 3 | PRGM=test 4 | 5 | MAIN: $(DEPS) 6 | $(CC) $(DEPS) -o $(PRGM) 7 | 8 | clean: 9 | rm *.o $(PRGM) 10 | 11 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | This is a very simple library for implementing a Min-Heap or Priority Queue 4 | in plain C. It should be very understandable, and is a useful reference for 5 | people who are learning C or want to understand the binary heap data structure. 6 | 7 | This implementation makes use of a plain array to store the table. It supports 8 | resizing (growing and shrinking) and does this by simply doubling or halving the 9 | array size when appropriate. This was make as a counter part to a different project, 10 | [c-minheap-indirect](https://github.com/armon/c-minheap-indirect) which utilizes 11 | an indirection table to increase memory efficiency. This is an important distinction 12 | when the number of elements is very large, as this implementation will have large amounts 13 | of wasted space, and relies on expensive doubling operations, while the indirect 14 | implementation has very cheap resizing operations and wastes less than a single 15 | page worth of memory. 16 | 17 | # Using the library 18 | 19 | To use the library you only need to include the heap.h header, and 20 | link against the heap.c file. There is nothing fancy required. 21 | 22 | # Testing the library 23 | 24 | Included in the project is a file main.c which serves as a simple test file. 25 | We randomly generate a large number of keys (variable, default to 10M), and 26 | insert them into our heap. We then extract the keys and verify they are ordered. 27 | To test this, just run `make` and then run the test program. 28 | 29 | 30 | -------------------------------------------------------------------------------- /heap.c: -------------------------------------------------------------------------------- 1 | /** 2 | * This file defines the methods declared in heap.h 3 | * These are used to create and manipulate a heap 4 | * data structure. 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "heap.h" 12 | 13 | // Helpful Macros 14 | #define LEFT_CHILD(i) ((i<<1)+1) 15 | #define RIGHT_CHILD(i) ((i<<1)+2) 16 | #define PARENT_ENTRY(i) ((i-1)>>1) 17 | #define SWAP_ENTRIES(parent,child) { \ 18 | void* temp = parent->key; \ 19 | parent->key = child->key; \ 20 | child->key = temp; \ 21 | temp = parent->value; \ 22 | parent->value = child->value; \ 23 | child->value = temp; \ 24 | } 25 | 26 | #define GET_ENTRY(index,table) ((heap_entry*)(table+index)) 27 | 28 | 29 | 30 | 31 | /** 32 | * Stores the number of heap_entry structures 33 | * we can fit into a single page of memory. 34 | * 35 | * This is determined by the page size, so we 36 | * need to determine this at run time. 37 | */ 38 | static int ENTRIES_PER_PAGE = 0; 39 | 40 | /** 41 | * Stores the number of bytes in a single 42 | * page of memory. 43 | */ 44 | static int PAGE_SIZE = 0; 45 | 46 | // Helper function to map a number of pages into memory 47 | // Returns NULL on error, otherwise returns a pointer to the 48 | // first page. 49 | static void* map_in_pages(int page_count) { 50 | // Check everything 51 | assert(page_count > 0); 52 | 53 | // Call mmmap to get the pages 54 | void* addr = mmap(NULL, page_count*PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); 55 | 56 | if (addr == MAP_FAILED) 57 | return NULL; 58 | else { 59 | // Clear the memory 60 | bzero(addr,page_count*PAGE_SIZE); 61 | 62 | // Return the address 63 | return addr; 64 | } 65 | } 66 | 67 | 68 | // Helper function to map a number of pages out of memory 69 | static void map_out_pages(void* addr, int page_count) { 70 | // Check everything 71 | assert(addr != NULL); 72 | assert(page_count > 0); 73 | 74 | // Call munmap to get rid of the pages 75 | int result = munmap(addr, page_count*PAGE_SIZE); 76 | 77 | // The result should be 0 78 | assert(result == 0); 79 | } 80 | 81 | 82 | // This is a comparison function that treats keys as signed ints 83 | int compare_int_keys(register void* key1, register void* key2) { 84 | // Cast them as int* and read them in 85 | register int key1_v = *((int*)key1); 86 | register int key2_v = *((int*)key2); 87 | 88 | // Perform the comparison 89 | if (key1_v < key2_v) 90 | return -1; 91 | else if (key1_v == key2_v) 92 | return 0; 93 | else 94 | return 1; 95 | } 96 | 97 | 98 | // Creates a new heap 99 | void heap_create(heap* h, int initial_size, int (*comp_func)(void*,void*)) { 100 | // Check if we need to setup our globals 101 | if (PAGE_SIZE == 0) { 102 | // Get the page size 103 | PAGE_SIZE = getpagesize(); 104 | 105 | // Calculate the max entries 106 | ENTRIES_PER_PAGE = PAGE_SIZE / sizeof(heap_entry); 107 | } 108 | 109 | // Check that initial size is greater than 0, else set it to ENTRIES_PER_PAGE 110 | if (initial_size <= 0) 111 | initial_size = ENTRIES_PER_PAGE; 112 | 113 | // If the comp_func is null, treat the keys as signed ints 114 | if (comp_func == NULL) 115 | comp_func = compare_int_keys; 116 | 117 | 118 | // Store the compare function 119 | h->compare_func = comp_func; 120 | 121 | // Set active entries to 0 122 | h->active_entries = 0; 123 | 124 | // Determine how many pages of entries we need 125 | h->allocated_pages = initial_size / ENTRIES_PER_PAGE + ((initial_size % ENTRIES_PER_PAGE > 0) ? 1 : 0); 126 | h->minimum_pages = h->allocated_pages; 127 | 128 | // Allocate the table 129 | h->table = (void*)map_in_pages(h->allocated_pages); 130 | } 131 | 132 | 133 | // Cleanup a heap 134 | void heap_destroy(heap* h) { 135 | // Check that h is not null 136 | assert(h != NULL); 137 | 138 | // Map out the table 139 | map_out_pages(h->table, h->allocated_pages); 140 | 141 | // Clear everything 142 | h->active_entries = 0; 143 | h->allocated_pages = 0; 144 | h->table = NULL; 145 | } 146 | 147 | 148 | // Gets the size of the heap 149 | int heap_size(heap* h) { 150 | // Return the active entries 151 | return h->active_entries; 152 | } 153 | 154 | 155 | // Gets the minimum element 156 | int heap_min(heap* h, void** key, void** value) { 157 | // Check the number of elements, abort if 0 158 | if (h->active_entries == 0) 159 | return 0; 160 | 161 | // Get the 0th element 162 | heap_entry* root = GET_ENTRY(0, h->table); 163 | 164 | // Set the key and value 165 | *key = root->key; 166 | *value = root->value; 167 | 168 | // Success 169 | return 1; 170 | } 171 | 172 | 173 | // Insert a new element 174 | void heap_insert(heap *h, void* key, void* value) { 175 | // Check if this heap is not destoyed 176 | assert(h->table != NULL); 177 | 178 | // Check if we have room 179 | int max_entries = h->allocated_pages * ENTRIES_PER_PAGE; 180 | if (h->active_entries + 1 > max_entries) { 181 | // Get the new number of entries we need 182 | int new_size = h->allocated_pages * 2; 183 | 184 | // Map in a new table 185 | heap_entry* new_table = map_in_pages(new_size); 186 | 187 | // Copy the old entries, copy the entire pages 188 | memcpy(new_table, h->table, h->allocated_pages*PAGE_SIZE); 189 | 190 | // Cleanup the old table 191 | map_out_pages(h->table, h->allocated_pages); 192 | 193 | // Switch to the new table 194 | h->table = new_table; 195 | h->allocated_pages = new_size; 196 | } 197 | 198 | // Store the comparison function 199 | int (*cmp_func)(void*,void*) = h->compare_func; 200 | 201 | // Store the table address 202 | heap_entry* table = h->table; 203 | 204 | // Get the current index 205 | int current_index = h->active_entries; 206 | heap_entry* current = GET_ENTRY(current_index, table); 207 | 208 | // Loop variables 209 | int parent_index; 210 | heap_entry *parent; 211 | 212 | // While we can, keep swapping with our parent 213 | while (current_index > 0) { 214 | // Get the parent index 215 | parent_index = PARENT_ENTRY(current_index); 216 | 217 | // Get the parent entry 218 | parent = GET_ENTRY(parent_index, table); 219 | 220 | // Compare the keys, and swap if we need to 221 | if (cmp_func(key, parent->key) < 0) { 222 | // Move the parent down 223 | current->key = parent->key; 224 | current->value = parent->value; 225 | 226 | // Move our reference 227 | current_index = parent_index; 228 | current = parent; 229 | 230 | // We are done swapping 231 | } else 232 | break; 233 | } 234 | 235 | // Insert at the current index 236 | current->key = key; 237 | current->value = value; 238 | 239 | // Increase the number of active entries 240 | h->active_entries++; 241 | } 242 | 243 | 244 | // Deletes the minimum entry in the heap 245 | int heap_delmin(heap* h, void** key, void** value) { 246 | // Check there is a minimum 247 | if (h->active_entries == 0) 248 | return 0; 249 | 250 | // Load in the map table 251 | heap_entry* table = h->table; 252 | 253 | // Get the root element 254 | int current_index = 0; 255 | heap_entry* current = GET_ENTRY(current_index, table); 256 | 257 | // Store the outputs 258 | *key = current->key; 259 | *value = current->value; 260 | 261 | // Reduce the number of active entries 262 | h->active_entries--; 263 | 264 | // Get the active entries 265 | int entries = h->active_entries; 266 | 267 | // If there are any other nodes, we may need to move them up 268 | if (h->active_entries > 0) { 269 | // Move the last element to the root 270 | heap_entry* last = GET_ENTRY(entries,table); 271 | current->key = last->key; 272 | current->value = last->value; 273 | 274 | // Loop variables 275 | heap_entry* left_child; 276 | heap_entry* right_child; 277 | 278 | // Load the comparison function 279 | int (*cmp_func)(void*,void*) = h->compare_func; 280 | 281 | // Store the left index 282 | int left_child_index; 283 | 284 | while (left_child_index = LEFT_CHILD(current_index), left_child_index < entries) { 285 | // Load the left child 286 | left_child = GET_ENTRY(left_child_index, table); 287 | 288 | // We have a left + right child 289 | if (left_child_index+1 < entries) { 290 | // Load the right child 291 | right_child = GET_ENTRY((left_child_index+1), table); 292 | 293 | // Find the smaller child 294 | if (cmp_func(left_child->key, right_child->key) <= 0) { 295 | 296 | // Swap with the left if it is smaller 297 | if (cmp_func(current->key, left_child->key) == 1) { 298 | SWAP_ENTRIES(current,left_child); 299 | current_index = left_child_index; 300 | current = left_child; 301 | 302 | // Otherwise, the current is smaller 303 | } else 304 | break; 305 | 306 | // Right child is smaller 307 | } else { 308 | 309 | // Swap with the right if it is smaller 310 | if (cmp_func(current->key, right_child->key) == 1) { 311 | SWAP_ENTRIES(current,right_child); 312 | current_index = left_child_index+1; 313 | current = right_child; 314 | 315 | // Current is smaller 316 | } else 317 | break; 318 | 319 | } 320 | 321 | 322 | // We only have a left child, only do something if the left is smaller 323 | } else if (cmp_func(current->key, left_child->key) == 1) { 324 | SWAP_ENTRIES(current,left_child); 325 | current_index = left_child_index; 326 | current = left_child; 327 | 328 | // Done otherwise 329 | } else 330 | break; 331 | 332 | } 333 | } 334 | 335 | // Check if we should release a page of memory 336 | int used_pages = entries / ENTRIES_PER_PAGE + ((entries % ENTRIES_PER_PAGE > 0) ? 1 : 0); 337 | 338 | // Allow one empty page, but not two 339 | if (h->allocated_pages / 2 > used_pages + 1 && h->allocated_pages / 2 >= h->minimum_pages) { 340 | // Get the new number of entries we need 341 | int new_size = h->allocated_pages / 2; 342 | 343 | // Map in a new table 344 | heap_entry* new_table = map_in_pages(new_size); 345 | 346 | // Copy the old entries, copy the entire pages 347 | memcpy(new_table, h->table, used_pages*PAGE_SIZE); 348 | 349 | // Cleanup the old table 350 | map_out_pages(h->table, h->allocated_pages); 351 | 352 | // Switch to the new table 353 | h->table = new_table; 354 | h->allocated_pages = new_size; 355 | } 356 | 357 | // Success 358 | return 1; 359 | } 360 | 361 | 362 | // Allows a user to iterate over all entries, e.g. to free() the memory 363 | void heap_foreach(heap* h, void (*func)(void*,void*)) { 364 | // Store the current index and max index 365 | int index = 0; 366 | int entries = h->active_entries; 367 | 368 | heap_entry* entry; 369 | heap_entry* table = h->table; 370 | 371 | for (;indexkey, entry->value); 377 | } 378 | } 379 | 380 | 381 | -------------------------------------------------------------------------------- /heap.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Armon Dadgar 3 | * 4 | * Header for the Heap functions and data definitions 5 | */ 6 | 7 | #ifndef HEAP_H 8 | #define HEAP_H 9 | 10 | // Structure for a single heap entry 11 | typedef struct heap_entry { 12 | void* key; // Key for this entry 13 | void* value; // Value for this entry 14 | } heap_entry; 15 | 16 | 17 | // Main struct for representing the heap 18 | typedef struct heap { 19 | int (*compare_func)(void*, void*); // The key comparison function to use 20 | int active_entries; // The number of entries in the heap 21 | int minimum_pages; // The minimum number of pages to maintain, based on the initial cap. 22 | int allocated_pages; // The number of pages in memory that are allocated 23 | heap_entry* table; // Pointer to the table, which maps to the pages 24 | } heap; 25 | 26 | // Functions 27 | 28 | /** 29 | * Creates a new heap 30 | * @param h Pointer to a heap structure that is initialized 31 | * @param initial_size What should the initial size of the heap be. If <= 0, then it will be set to the minimum 32 | * permissable size, of 1 page (512 entries on 32bit system with 4K pages). 33 | * @param comp_func A pointer to a function that can be used to compare the keys. If NULL, it will be set 34 | * to a function which treats keys as signed ints. This function must take two keys, given as pointers and return an int. 35 | * It should return -1 if key 1 is smaller, 0 if they are equal, and 1 if key 2 is smaller. 36 | */ 37 | void heap_create(heap* h, int initial_size, int (*comp_func)(void*,void*)); 38 | 39 | /** 40 | * Returns the size of the heap 41 | * @param h Pointer to a heap structure 42 | * @return The number of entries in the heap. 43 | */ 44 | int heap_size(heap* h); 45 | 46 | /** 47 | * Inserts a new element into a heap. 48 | * @param h The heap to insert into 49 | * @param key The key of the new entry 50 | * @param value The value of the new entry 51 | */ 52 | void heap_insert(heap* h, void* key, void* value); 53 | 54 | /** 55 | * Returns the element with the smallest key in the heap. 56 | * @param h Pointer to the heap structure 57 | * @param key A pointer to a pointer, to set to the minimum key 58 | * @param value Set to the value corresponding with the key 59 | * @return 1 if the minimum element exists and is set, 0 if there are no elements. 60 | */ 61 | int heap_min(heap* h, void** key, void** value); 62 | 63 | /** 64 | * Deletes the element with the smallest key from the heap. 65 | * @param h Pointer to the heap structure 66 | * @param key A pointer to a pointer, to set to the minimum key 67 | * @param valu Set to the value corresponding with the key 68 | * @return 1if the minimum element exists and is deleted, 0 if there are no elements. 69 | */ 70 | int heap_delmin(heap* h, void** key, void** value); 71 | 72 | /** 73 | * Calls a function for each entry in the heap. 74 | * @param h The heap to iterate over 75 | * @param func The function to call on each entry. Should take a void* key and value. 76 | */ 77 | void heap_foreach(heap* h, void (*func)(void*,void*)); 78 | 79 | /** 80 | * Destroys and cleans up a heap. 81 | * @param h The heap to destroy. 82 | */ 83 | void heap_destroy(heap* h); 84 | 85 | #endif 86 | 87 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "heap.h" 8 | 9 | int main(int argc, char** argv) { 10 | 11 | // Create the heap 12 | heap h; 13 | heap_create(&h,0,NULL); 14 | 15 | // Maximum 16 | int count = 10000000; // 10M 17 | if (argc > 1) 18 | count = atoi(argv[1]); // Get the count as an argument 19 | printf("Sorting array of %d random entries.\n", count); 20 | 21 | // Allocate a key and value 22 | int* key = (int*)malloc(count*sizeof(int)); 23 | char* value = "The meaning of life."; 24 | 25 | // Initialize the first key 26 | unsigned int val = 42; 27 | srand( val); 28 | printf("Seed %d\n",val); 29 | 30 | // Store that as the minimum 31 | int min = INT_MAX; 32 | 33 | // Use a pseudo-random generator for the other keys 34 | for (int i=0;i *min_key) { 58 | printf("Previous key is greater than current key!\n"); 59 | } 60 | prev_key = min_key; 61 | } 62 | 63 | // Clean up the heap 64 | heap_destroy(&h); 65 | } 66 | 67 | --------------------------------------------------------------------------------