Skip to content
Snippets Groups Projects
heap.c 7.77 KiB
Newer Older
 * Dr. Sheaffer's Heap Code
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
struct heap_node {
  heap_node_t *next;
  heap_node_t *prev;
  heap_node_t *parent;
  heap_node_t *child;
  void *datum;
  uint32_t degree;
  uint32_t mark;
};

#define swap(a, b) ({    \
  typeof (a) _tmp = (a); \
  (a) = (b);             \
  (b) = _tmp;            \
})

#define splice_heap_node_lists(n1, n2) ({ \
  if ((n1) && (n2)) {                     \
    (n1)->next->prev = (n2)->prev;        \
    (n2)->prev->next = (n1)->next;        \
    (n1)->next = (n2);                    \
    (n2)->prev = (n1);                    \
  }                                       \
})

#define insert_heap_node_in_list(n, l) ({ \
  (n)->next = (l);                        \
  (n)->prev = (l)->prev;                  \
  (n)->prev->next = (n);                  \
  (l)->prev = (n);                        \
})

#define remove_heap_node_from_list(n) ({ \
  (n)->next->prev = (n)->prev;           \
  (n)->prev->next = (n)->next;           \
})

void print_heap_node(heap_node_t *n, unsigned indent,
                     char *(*print)(const void *v))
{
  heap_node_t *nc;

  printf("%*s%s\n", indent, "", print(n->datum));
  if (!(nc = n->child)) {
    return;
  }

  do {
    print_heap_node(nc, indent + 2, print);
    nc = nc->next;
  } while (nc != n->child);
void print_heap(heap_t *h, char *(*print)(const void *v))
{
  heap_node_t *n;

  if (h->min) {
    printf("size = %u\n", h->size);
    printf("min = ");
    n = h->min;
    do {
      print_heap_node(n, 0, print);
      n = n->next;
    } while (n != h->min);
  } else {
    printf("(null)\n");
  }
void print_heap_node_list(heap_node_t *n)
{
  heap_node_t *hn;

  if (!n) {
    return;
  }

  hn = n;
  do {
    printf("%p ", hn->datum);
    hn = hn->next;
  } while (hn != n);
  printf("\n");
}

void heap_init(heap_t *h,
               int32_t (*compare)(const void *key, const void *with),
               void (*datum_delete)(void *))
{
  h->min = NULL;
  h->size = 0;
  h->compare = compare;
  h->datum_delete = datum_delete;
}

void heap_node_delete(heap_t *h, heap_node_t *hn)
{
  heap_node_t *next;

  hn->prev->next = NULL;
  while (hn) {
    if (hn->child) {
      heap_node_delete(h, hn->child);
    } 
    next = hn->next;
    if (h->datum_delete) {
      h->datum_delete(hn->datum);
    free(hn);
    hn = next;
  }
}

void heap_delete(heap_t *h)
{
  if (h->min) {
    heap_node_delete(h, h->min);
  }
  h->min = NULL;
  h->size = 0;
  h->compare = NULL;
  h->datum_delete = NULL;
}

heap_node_t *heap_insert(heap_t *h, void *v)
{
  heap_node_t *n;

  assert((n = calloc(1, sizeof (*n))));
  n->datum = v;

  if (h->min) {
    insert_heap_node_in_list(n, h->min);
  } else {
    n->next = n->prev = n;
  }
  if (!h->min || (h->compare(v, h->min->datum) < 0)) {
    h->min = n;

  return n;
}

void *heap_peek_min(heap_t *h)
{
  return h->min ? h->min->datum : NULL;
}

static void heap_link(heap_t *h, heap_node_t *node, heap_node_t *root)
{
  /*  remove_heap_node_from_list(node);*/
  if (root->child) {
    insert_heap_node_in_list(node, root->child);
  } else {
    root->child = node;
    node->next = node->prev = node;
  node->parent = root;
  root->degree++;
  node->mark = 0;
static void heap_consolidate(heap_t *h)
{
  uint32_t i;
  heap_node_t *x, *y, *n;
  heap_node_t *a[64]; /* Need ceil(lg(h->size)), so this is good  *
                       * to the limit of a 64-bit address space,  *
                       * and much faster than any lg calculation. */
  memset(a, 0, sizeof (a));

  h->min->prev->next = NULL;

  for (x = n = h->min; n; x = n) {
    n = n->next;

    while (a[x->degree]) {
      y = a[x->degree];
      if (h->compare(x->datum, y->datum) > 0) {
        swap(x, y);
      }
      a[x->degree] = NULL;
      heap_link(h, y, x);
    }
    a[x->degree] = x;

  for (h->min = NULL, i = 0; i < 64; i++) {
    if (a[i]) {
      if (h->min) {
        insert_heap_node_in_list(a[i], h->min);
        if (h->compare(a[i]->datum, h->min->datum) < 0) {
          h->min = a[i];
        }
      } else {
        h->min = a[i];
        a[i]->next = a[i]->prev = a[i];
      }
  }
}

void *heap_remove_min(heap_t *h)
{
  void *v;
  heap_node_t *n;

  v = NULL;

  if (h->min) {
    v = h->min->datum;
    if (h->size == 1) {
      free(h->min);
      h->min = NULL;
    } else {
      if ((n = h->min->child)) {
        for (; n->parent; n = n->next) {
          n->parent = NULL;
        }
      }

      splice_heap_node_lists(h->min, h->min->child);

      n = h->min;
      remove_heap_node_from_list(n);
      h->min = n->next;
      free(n);

      heap_consolidate(h);

    h->size--;
  }

  return v;
}

int heap_combine(heap_t *h, heap_t *h1, heap_t *h2)
{
  if (h1->compare != h2->compare ||
      h1->datum_delete != h2->datum_delete) {
    return 1;
  }

  h->compare = h1->compare;
  h->datum_delete = h1->datum_delete;

  if (!h1->min) {
    h->min = h2->min;
    h->size = h2->size;
  } else if (!h2->min) {
    h->min = h1->min;
    h->size = h1->size;
  } else {
    h->min = ((h->compare(h1->min->datum, h2->min->datum) < 0) ?
              h1->min                                          :
              h2->min);
    splice_heap_node_lists(h1->min, h2->min);
  }

  memset(h1, 0, sizeof (*h1));
  memset(h2, 0, sizeof (*h2));

  return 0;
}

static void heap_cut(heap_t *h, heap_node_t *n, heap_node_t *p)
{
  if (!--p->degree) {
    p->child = NULL;
  }
  if (p->child == n) {
    p->child = p->child->next;
  }
  remove_heap_node_from_list(n);
  n->parent = NULL;
  n->mark = 0;
  insert_heap_node_in_list(n, h->min);
}

static void heap_cascading_cut(heap_t *h, heap_node_t *n)
{
  heap_node_t *p;

  if ((p = n->parent)) {
    if (!n->mark) {
      n->mark = 1;
      heap_cut(h, n, p);
      heap_cascading_cut(h, n);
}

int heap_decrease_key(heap_t *h, heap_node_t *n, void *v)
{
  if (h->compare(n->datum, v) <= 0) {
    return 1;
  }

  if (h->datum_delete) {
    h->datum_delete(n->datum);
  }
  n->datum = v;

  return heap_decrease_key_no_replace(h, n);
}

int heap_decrease_key_no_replace(heap_t *h, heap_node_t *n)
{
  /* No tests that the value hasn't actually increased.  Change *
   * occurs in place, so the check is not possible here.  The   *
   * user is completely responsible for ensuring that they      *
   * don't fubar the queue.                                     */

  heap_node_t *p;

  p = n->parent;

  if (p && (h->compare(n->datum, p->datum) < 0)) {
    heap_cut(h, n, p);
    heap_cascading_cut(h, p);
  }
  if (h->compare(n->datum, h->min->datum) < 0) {
    h->min = n;
  }

#ifdef TESTING

int32_t compare(const void *key, const void *with)
{
  return *((int *) key) - *((int *) with);
}

char *print_int(const void *v)
{
  static char out[640];

  snprintf(out, 640, "%d", *((int *) v));

  return out;
}

int main(int argc, char *argv[])
{
  heap_t h;
  int **keys;
  heap_node_t **a;
  /*  int *p;*/
  int i, j;
  int n;

  if (argc == 2) {
    n = atoi(argv[1]);
  } else {
    n = 20;
  }

  assert(keys = calloc(n, sizeof (*keys)));
  assert(a = calloc(n, sizeof (*a)));

  heap_init(&h, compare, free);

  for (i = 0; i < n; i++) {
    assert(keys[i] = malloc(sizeof (*keys[i])));
    *keys[i] = i;
    a[i] = heap_insert(&h, keys[i]);
  }

  print_heap(&h, print_int);
  printf("------------------------------------\n");
  
  heap_remove_min(&h);
  assert(keys[0] = malloc(sizeof (*keys[0])));
  *keys[0] = 0;
  a[0] = heap_insert(&h, keys[0]);
  for (i = 0; i < 100 * n; i++) {
    j = rand() % n;
    /*    assert(p = malloc (sizeof (*p)));*/
    (*(int *) a[j]->datum)--;
    /*    (*p)--;*/
    heap_decrease_key_no_replace(&h, a[j]);
    print_heap(&h, print_int);
    printf("------------------------------------\n");
  }

  free(keys);

  return 0;