heap: small binary heap implementation
authorMarko Kreen <markokr@gmail.com>
Wed, 15 Apr 2009 12:43:11 +0000 (15:43 +0300)
committerMarko Kreen <markokr@gmail.com>
Wed, 15 Apr 2009 16:25:12 +0000 (19:25 +0300)
Useful for keeping track of timeouts.

usual/heap-impl.h [new file with mode: 0644]

diff --git a/usual/heap-impl.h b/usual/heap-impl.h
new file mode 100644 (file)
index 0000000..f97fbe8
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * Binary Heap.
+ *
+ * Copyright (c) 2009  Marko Kreen, Skype Technologies OÜ
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Summary - binary heap is sort of binary tree held inside array,
+ * with following 2 properties:
+ * - heap property: each node is "better" than it's childs.
+ * - shape property: binary tree is complete, meaning all levels
+ *   except the last one are fully filled.
+ *
+ * Instead of "min"- or "max"-heap, this is "best"-heap,
+ * as it operates with user-defined IS_BETTER macro,
+ * which is used to bubble elements on top.  Main reason
+ * for such design is to make the comparisions inline.
+ */
+
+/* #define IS_BETTER(p1, p2) */
+#ifndef IS_BETTER
+#error <usual/heap.h> requires IS_BETTER(p1, p2) macro to be defined.
+#endif
+
+/*
+ * If user wants to delete elements from the middle of heap,
+ * this macro should be used to keep track where the element
+ * is located.
+ */
+#ifndef SAVE_POS
+#define SAVE_POS(ptr, pos)
+#endif
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+struct Heap {
+       void **data;
+       unsigned allocated;
+       unsigned used;
+};
+
+
+/*
+ * Low-level operations.
+ */
+
+static inline unsigned _heap_get_parent(unsigned i)
+{
+       return (i - 1) / 2;
+}
+
+static inline unsigned _heap_get_child(unsigned i, unsigned child_nr)
+{
+       return 2*i + 1 + child_nr;
+}
+
+static inline bool _heap_is_better(struct Heap *h, unsigned i1, unsigned i2)
+{
+       return IS_BETTER(h->data[i1], h->data[i2]);
+}
+
+static inline void _heap_set(struct Heap *h, unsigned i, void *ptr)
+{
+       h->data[i] = ptr;
+       SAVE_POS(ptr, i);
+}
+
+static inline void _heap_swap(struct Heap *h, unsigned i1, unsigned i2)
+{
+       void *tmp = h->data[i1];
+       _heap_set(h, i1, h->data[i2]);
+       _heap_set(h, i2, tmp);
+}
+
+static void _heap_bubble_up(struct Heap *h, unsigned i)
+{
+       unsigned p;
+       while (i > 0) {
+               p = _heap_get_parent(i);
+               if (!_heap_is_better(h, i, p))
+                       break;
+               _heap_swap(h, i, p);
+               i = p;
+       }
+}
+
+static void _heap_bubble_down(struct Heap *h, unsigned i)
+{
+       unsigned c = _heap_get_child(i, 0);
+       while (c < h->used) {
+               if (c + 1 < h->used) {
+                       if (_heap_is_better(h, c + 1, c))
+                               c = c + 1;
+               }
+               if (!_heap_is_better(h, c, i))
+                       break;
+               _heap_swap(h, i, c);
+               i = c;
+               c = _heap_get_child(i, 0);
+       }
+}
+
+static bool _heap_make_room(struct Heap *h, unsigned extra)
+{
+       void *tmp;
+       unsigned newalloc;
+
+       if (h->used + extra < h->allocated)
+               return true;
+
+       newalloc = h->allocated * 2;
+       if (newalloc < 32)
+               newalloc = 32;
+       if (newalloc < h->used + extra)
+               newalloc = h->used + extra;
+
+       tmp = realloc(h->data, newalloc * sizeof(void *));
+       if (!tmp)
+               return false;
+       h->data = tmp;
+       h->allocated = newalloc;
+       return true;
+}
+
+
+/*
+ * Actual API.
+ */
+
+
+static void heap_init(struct Heap *h)
+{
+       h->data = NULL;
+       h->allocated = 0;
+       h->used = 0;
+}
+
+static void heap_destroy(struct Heap *h)
+{
+       free(h->data);
+       heap_init(h);
+}
+
+static inline void *heap_get_top(struct Heap *h)
+{
+       return (h->used > 0) ? h->data[0] : NULL;
+}
+
+static inline bool heap_reserve(struct Heap *h, unsigned extra)
+{
+       if (h->used + extra < h->allocated)
+               return true;
+
+       return _heap_make_room(h, extra);
+}
+
+static bool heap_insert(struct Heap *h, void *ptr)
+{
+       unsigned i;
+
+       if (!heap_reserve(h, 1))
+               return false;
+
+       i = h->used++;
+       _heap_set(h, i, ptr);
+       _heap_bubble_up(h, i);
+       return true;
+}
+
+static void heap_delete_pos(struct Heap *h, unsigned pos)
+{
+       unsigned last;
+
+       if (pos >= h->used)
+               return;
+
+       last = --h->used;
+       if (pos == last) {
+               h->data[last] = NULL;
+               return;
+       }
+
+       _heap_set(h, pos, h->data[last]);
+       h->data[last] = NULL;
+
+       if (pos > 0 && _heap_is_better(h, pos, _heap_get_parent(pos)))
+               _heap_bubble_up(h, pos);
+       else
+               _heap_bubble_down(h, pos);
+}
+
+static inline void heap_delete_top(struct Heap *h)
+{
+       heap_delete_pos(h, 0);
+}
+