A GENERIC HEAPSORT ALGORITHM IN C
by Stephen Russell

[LISTING ONE]

/*
*  The Heapsort to sort an array of n integers.
*/

static
fixheap(h, i, n)
int *h;
unsigned i, n;
{
       unsigned k;
       int      tmp;

       while ((k = 2 * i) <= n)                /* h[k] = left child of h[i] */
       {
               /*  Find maximum of left and right children */

               if (k != n && h[k+1] > h[k])
                       ++k;                    /* right child is greater */

               /* Compare greater of children to parent */

               if (h[i] >= h[k])
                       return;

               /* Parent is less than child, so swap */

               tmp = h[k]; h[k] = h[i]; h[i] = tmp;

               i = k;                          /* move down tree */
       }
}


hsort(h, n)
int *h;
unsigned n;
{
       unsigned i;
       int      tmp;

       --h;                    /* adjust for zero-origin arrays in C */

       for (i = n/2; i > 1; --i)
               fixheap(h, i, n);       /* build heap, except for h[1] */

       while (n > 1)
       {
               fixheap(h, 1, n);       /* move max to h[1] */
               tmp = h[1];             /* move max to final position */
               h[1] = h[n];
               h[n] = tmp;
               --n;                    /* reduce size of heap */
       }
}

[LISTING TWO]


/*
*      Generic Heapsort, derived from listing 1.
*/


#define H(k)    (h + k * size)

static
swap(p1, p2, n)                 /* swap n bytes */
char *p1, *p2;
unsigned n;
{
       char    tmp;

       while (n-- != 0)
       {
               tmp = *p1; *p1++ = *p2; *p2++ = tmp;
       }
}

static
fixheap(h, size, cmp, i, n)
char *h;
unsigned size, i, n;
int (*cmp)();
{
       unsigned k;

       while ((k = 2 * i) <= n)
       {
               if (k != n && (*cmp)(H(k+1), H(k)) > 0)
                       ++k;

               if ((*cmp)(H(i), H(k)) >= 0)
                       return;

               swap(H(i), H(k), size);
               i = k;
       }
}

hsort(h, n, size, cmp)
char *h;
unsigned n, size;
int (*cmp)();
{
       unsigned i;

       h -= size;

       for (i = n/2; i > 1; --i)
               fixheap(h, size, cmp, i, n);

       while (n > 1)
       {
               fixheap(h, size, cmp, 1, n);
               swap(H(1), H(n), size);
               --n;
       }
}


[LISTING THREE]

/*
*  Generic Heapsort.
*
*  Synopsis:
*      hsort(char *base, unsigned n, unsigned size, int (*fn)())
*  Description:
*      Hsort sorts the array of `n' items which starts at address `base'.
*      The size of each item is as given.  Items are compared by the function
*      `fn', which is passed pointers to two items as arguments. The function
*      should return < 0 if item1 < item2, == 0 if item1 == item2, and > 0
*      if item1 > item2.
*  Version:
*      1988 April 28
*  Author:
*      Stephen Russell, Department of Computer Science,
*      University of Sydney, 2006
*      Australia.
*/

#ifdef INLINE

#define swap(p1, p2, n) {\
       register char *_p1, *_p2;\
       register unsigned _n;\
       register char _tmp;\
\
       for (_p1 = p1, _p2 = p2, _n = n; _n-- > 0; )\
       {\
               _tmp = *_p1; *_p1++ = *_p2; *_p2++ = _tmp;\
       }\
}\

#else

/*
*   Support routine for swapping elements of the array.
*/

static
swap(p1, p2, n)
register char    *p1, *p2;
register unsigned n;
{
       register char   ctmp;

       /*
        *  On machines with no alignment restrictions for int's,
        *  the following loop may improve performance if moving lots
        *  of data. It has been commented out for portability.

        register int   itmp;

        for ( ; n > sizeof(int); n -= sizeof(int))
        {
               itmp = *(int *)p1;
               *(int *)p1 = *(int *)p2;
               p1 += sizeof(int);
               *(int *)p2 = itmp;
               p2 += sizeof(int);
       }

       */

       while (n-- != 0)
       {
               ctmp = *p1; *p1++ = *p2; *p2++ = ctmp;
       }
}

#endif

/*
*      To avoid function calls in the inner loops, the code responsible for
*      constructing a heap from (part of) the array has been expanded inline.
*      It is possible to convert this common code to a function. The three
*      parameters base0, cmp and size are invariant - only the size of the
*      gap and the high bound of the array change. In phase 1, gap decreases
*      while hi is fixed. In phase 2, gap == size, and hi decreases. The
*      variables p, q, and g are only used in this common code.
*/

hsort(base, n, size, cmp)
char *base;
unsigned n;
unsigned size;
int (*cmp)();
{
       register char    *p, *q, *base0, *hi;
       register unsigned gap, g;

       if (n < 2)
               return;

       base0 = base - size;            /* set up address of h[0] */

       /*
        *  The gap is the distance, in bytes, between h[0] and h[i],
        *  for some i. It is also the distance between h[i] and h[2*i];
        *  that is, the distance between a node and its left child.
        *  The initial node of interest is h[n/2] (the rightmost
        *  interior node), so gap is set accordingly. The following is
        *  the only multiplication needed.
        */

       gap = (n >> 1) * size;          /* initial gap is n/2*size */
       hi = base0 + gap + gap;         /* calculate address of h[n] */
       if (n & 1)
               hi += size;             /* watch out for odd arrays */

       /*
        *  Phase 1: Construct heap from random data.
        *
        *  For i = n/2 downto 2, ensure h[i] is greater than its
        *  children h[2*i] and h[2*i+1]. By decreasing 'gap' at each
        *  iteration, we move down the heap towards h[2]. The final step
        *  of making h[1] the maximum value is done in the next phase.
        */

       for ( ; gap != size; gap -= size)
       {
               /*  fixheap(base0, size, cmp, gap, hi) */

               for (p = base0 + (g = gap); (q = p + g) <= hi; p = q)
               {
                       g += g;         /* double gap for next level */

                       /*
                        *  Find greater of left and right children.
                        */

                       if (q != hi && (*cmp)(q + size, q) > 0)
                       {
                               q += size;      /* choose right child */
                               g += size;      /* follow right subtree */
                       }

                       /*
                        *  Compare with parent.
                        */

                       if ((*cmp)(p, q) >= 0)
                               break;          /* order is correct */

                       swap(p, q, size);       /* swap parent and child */
               }
       }

       /*
        *  Phase 2: Each iteration makes the first item in the
        *  array the maximum, then swaps it with the last item, which
        *  is its correct position. The size of the heap is decreased
        *  each iteration. The gap is always "size", as we are interested
        *  in the heap starting at h[1].
        */

       for ( ; hi != base; hi -= size)
       {
               /* fixheap(base0, size, cmp, gap (== size), hi) */

               p = base;               /* == base0 + size */
               for (g = size; (q = p + g) <= hi; p = q)
               {
                       g += g;
                       if (q != hi && (*cmp)(q + size, q) > 0)
                       {
                               q += size;
                               g += size;
                       }

                       if ((*cmp)(p, q) >= 0)
                               break;

                       swap(p, q, size);
               }

               swap(base, hi, size);           /* move largest item to end */
       }
}

[LISTING FOUR]


/*
*      Use hsort() to sort an array of strings read from input.
*/

#include        <stdio.h>


#define MAXN    500
#define MAXSTR  1000


cmp(p1, p2)
char **p1, **p2;
{
       return strcmp(*p1, *p2);
}

static  char    *string[MAXN];
static  char    buf[MAXSTR];

extern  char    *gets();
extern  char    *malloc();


main()
{
       char    *p;
       int     i, n;

       for (n = 0; gets(buf); ++n)
       {
               if (n == MAXN)
               {
                       fprintf(stderr, "Too many strings\n");
                       exit(1);
               }

               p = malloc(strlen(buf) + 1);
               if (p == (char *)NULL)
               {
                       fprintf(stderr, "Out of memory\n");
                       exit(2);
               }

               strcpy(string[n] = p, buf);
       }

       hsort(string, n, sizeof string[0], cmp);

       for (i = 0; i < n; ++i)
               puts(string[i]);

       exit(0);
}