部分GNU代码片 10、二叉堆(Binary Heap)

来源:互联网 发布:javascript初学者 编辑:程序博客网 时间:2024/04/27 21:13

n个关键字序列Kl,K2,…,Kn称为堆,当且仅当该序列满足如下性质(简称为堆性质):
(1) a[i]≤a[i*2]且a[i]≤a[2*i+1] 或(2)a[i]≥a[i*2]且a[i]≥a[2*i+1](1≤i≤堆的元素个数  )

struct binheap {
 unsigned  magic;
#define BINHEAP_MAGIC  0xf581581aU /* from /dev/random */
 void   *priv;
 binheap_cmp_t  *cmp;
 binheap_update_t *update;
 void   **array;
 unsigned  length;
 unsigned  next;
 unsigned  granularity;
};

#define PARENT(u) ((u) / 2)
#define CHILD(u,n) ((u) * 2 + (n))

/* Implementation ----------------------------------------------------*/

static void
binheap_update(struct binheap *bh, unsigned u)
{
 assert(bh->magic == BINHEAP_MAGIC);
 assert(u < bh->next);
 assert(bh->array[u] != NULL);
 if (bh->update == NULL)
  return;
 bh->update(bh->priv, bh->array[u], u);
}

struct binheap *
binheap_new(void *priv, binheap_cmp_t *cmp_f, binheap_update_t *update_f)
{
 struct binheap *bh;

 bh = calloc(sizeof *bh, 1);
 if (bh == NULL)
  return (bh);
 bh->priv = priv;
 bh->cmp = cmp_f;
 bh->update = update_f;
 bh->next = ROOT_IDX;
 bh->length = MIN_LENGTH;
 bh->array = calloc(sizeof *bh->array, bh->length);
 assert(bh->array != NULL);
 bh->granularity = getpagesize() / sizeof *bh->array;
 bh->magic = BINHEAP_MAGIC;
 return (bh);
}

static void
binhead_swap(struct binheap *bh, unsigned u, unsigned v)
{
 void *p;

 assert(bh->magic == BINHEAP_MAGIC);
 assert(u < bh->next);
 assert(v < bh->next);
 p = bh->array[u];
 bh->array[u] = bh->array[v];
 bh->array[v] = p;
 binheap_update(bh, u);
 binheap_update(bh, v);
}

static unsigned
binheap_trickleup(struct binheap *bh, unsigned u)
{
 unsigned v;

 assert(bh->magic == BINHEAP_MAGIC);
 while (u > ROOT_IDX) {
  v = PARENT(u);
  if (bh->cmp(bh->priv, bh->array[u], bh->array[v])) {
   binhead_swap(bh, u, v);
   u = v;
  } else
   break;
 }
 return (u);
}

static void
binheap_trickledown(struct binheap *bh, unsigned u)
{
 unsigned v1, v2;

 assert(bh->magic == BINHEAP_MAGIC);
 while (1) {
  v1 = CHILD(u, 0);
  v2 = CHILD(u, 1);
  if (v1 >= bh->next)
   return;
  if (v2 >= bh->next) {
   if (!bh->cmp(bh->priv, bh->array[u], bh->array[v1]))
    binhead_swap(bh, u, v1);
   return;
  }
  if (bh->cmp(bh->priv, bh->array[v1], bh->array[v2])) {
   if (!bh->cmp(bh->priv, bh->array[u], bh->array[v1])) {
    binhead_swap(bh, u, v1);
    u = v1;
    continue;
   }
  } else {
   if (!bh->cmp(bh->priv, bh->array[u], bh->array[v2])) {
    binhead_swap(bh, u, v2);
    u = v2;
    continue;
   }
  }
  return;
 }
}

void
binheap_insert(struct binheap *bh, void *p)
{
 unsigned u;

 assert(bh != NULL);
 assert(bh->magic == BINHEAP_MAGIC);
 assert(bh->length >= bh->next);
 if (bh->length == bh->next) {
  if (bh->length >= bh->granularity * 32)
   bh->length += bh->granularity * 32;
  else if (bh->length > bh->granularity)
   bh->length += bh->granularity;
  else
   bh->length += bh->length;
  bh->array = realloc(bh->array, bh->length * sizeof *bh->array);
  assert(bh->array != NULL);
 }
 u = bh->next++;
 bh->array[u] = p;
 binheap_update(bh, u);
 binheap_trickleup(bh, u);
}

void *
binheap_root(struct binheap *bh)
{

 assert(bh != NULL);
 assert(bh->magic == BINHEAP_MAGIC);
 if(bh->next == ROOT_IDX)
  return (NULL);
 return (bh->array[ROOT_IDX]);
}

void
binheap_delete(struct binheap *bh, unsigned idx)
{

 assert(bh != NULL);
 assert(bh->magic == BINHEAP_MAGIC);
 assert(bh->next > ROOT_IDX);
 assert(idx < bh->next);
 assert(idx > 0);
 assert(bh->array[idx] != NULL);
 bh->update(bh->priv, bh->array[idx], 0);
 if (idx == --bh->next) {
  bh->array[bh->next] = NULL;
  return;
 }
 bh->array[idx] = bh->array[bh->next];
 bh->array[bh->next] = NULL;
 binheap_update(bh, idx);
 idx = binheap_trickleup(bh, idx);
 binheap_trickledown(bh, idx);
 /* XXX: free part of array ? */
}


#ifdef TEST_DRIVER
/* Test driver -------------------------------------------------------*/

#include <stdio.h>

#if 0

static int
cmp(void *priv, void *a, void *b)
{

 return (*(unsigned *)a < *(unsigned *)b);
}

void
update(void *priv, void *a, unsigned u)
{
 printf("%p is now %u/n", a, u);
}

static void
dump(struct binheap *bh, const char *what, unsigned ptr)
{
 FILE *f;
 unsigned u, *up;

 printf("dump/n");
 f = popen("dot -Tps >> /tmp/_.ps", "w");
 assert(f != NULL);
 fprintf(f, "digraph binheap {/n");
 fprintf(f, "size=/"7,10/"/n");
 fprintf(f, "ptr [label=/"%s/"]/n", what);
 fprintf(f, "ptr -> node_%u/n", ptr);
 for (u = 1; u < bh->next; u++) {
  up = bh->array[u];
  fprintf(f, "node_%u [label=/"%u/"];/n", u, *up);
  if (u > 0)
   fprintf(f, "node_%u -> node_%u/n", PARENT(u), u);
 }
 fprintf(f, "}/n");
 pclose(f);
}

#define N 31
int
main(int argc, char **argv)
{
 struct binheap *bh;
 unsigned l[N], u, *up, lu;

 system("echo %! > /tmp/_.ps");
 bh = binheap_new(NULL, cmp, update);
 for (u = 0; u < N; u++) {
  l[u] = random() % 1000;
  binheap_insert(bh, &l[u]);
  if (1)
   dump(bh, "Insert", 0);
 }
 printf("Inserts done/n");
 lu = 0;
 while(1) {
  up = binheap_root(bh);
  if (up == NULL)
   break;
  assert(*up >= lu);
  lu = *up;
  u = random() % bh->next;
  binheap_delete(bh, u);
  if (1)
   dump(bh, "Delete", u);
 }
 printf("Deletes done/n");

 return (0);
}
#else

struct foo {
 unsigned idx;
 unsigned key;
};

#define M 1311191
#define N 131

struct foo ff[N];


static int
cmp(void *priv, void *a, void *b)
{
 struct foo *fa, *fb;

 fa = a;
 fb = b;
 return (fa->key < fb->key);
}

void
update(void *priv, void *a, unsigned u)
{
 struct foo *fa;

 fa = a;
 fa->idx = u;
}

void
chk(struct binheap *bh)
{
 unsigned u, v, nb = 0;
 struct foo *fa, *fb;

 for (u = 2; u < bh->next; u++) {
  v = PARENT(u);
  fa = bh->array[u];
  fb = bh->array[v];
  assert(fa->key > fb->key);
  continue;
  printf("[%2u/%2u] %10u > [%2u/%2u] %10u %s/n",
      u, fa - ff, fa->key,
      v, fb - ff, fb->key,
      fa->key > fb->key ? "OK" : "BAD");
  if (fa->key <= fb->key)
   nb++;
 }
 if (nb)
  exit(0);
}

int
main(int argc, char **argv)
{
 struct binheap *bh;
 unsigned u, v;

#if 0
 srandomdev();
 u = random();
 printf("Seed %u/n", u);
 srandom(u);
#endif
 bh = binheap_new(NULL, cmp, update);
 for (u = 0; u < M; u++) {
  v = random() % N;
  if (ff[v].idx > 0) {
   printf("Delete [%u] %'u/n", v, ff[v].key);
   binheap_delete(bh, ff[v].idx);
  } else {
   ff[v].key = random();
   printf("Insert [%u] %'u/n", v, ff[v].key);
   binheap_insert(bh, &ff[v]);
  }
  chk(bh);
 }
 return (0);