[comp.sys.mac.programmer] hsort.c

usenet@nlm.nih.gov (usenet news poster) (09/29/90)

Here's source code for a "portable" heap sort function with best- and worst-
case performance of O(NlogN).  Perhaps someone has time to compare its
performance with THINK C's qsort and Haydn Huntley's fastQsort.
I've found it to be about half the speed of an average BSD UNIX(TM) qsort
invocation for large lists.

Cheers,

Warren Gish
National Center for Biotechnology Information
National Library of Medicine
gish@ncbi.nlm.nih.gov

/****************************************************************************
	hsort -- sort a list using an heap sort algorithm

	This software is categorized as "United States Government Work"
	and is hereby entered into the public domain under the terms
	of the United States Copyright Act.  Restrictions can not be
	placed on its present or future use.  This software is presented
	"as is" with no warranty of any kind either expressed or implied.
****************************************************************************/

static void heapify (char *b0, char *b, char *lim, char *last, long w, int (*compar)());

void hsort(base, nel, width, compar)
	register char	*base;	/* Base address of array */
	long	nel;		/* No. of elements in array */
	long	width;		/* Width in bytes of each element in array */
	int	(*compar)();	/* Element comparison routine */
{
	register long	i;
	register char	ch;
	register char 	*base0 = base, *lim, *basef;

	lim = &base[((nel-2)/2)*width];
	basef = &base[(nel-1)*width];
	for (base = &base0[(nel/2 - 1)*width]; base >= base0; base -= width)
		heapify(base0, base, lim, basef, width, compar);

	for (base = &base0[(nel-1)*width]; base > base0; base -= width) {
		for (i=0; i<width; ++i) {
			ch = base0[i];
			base0[i] = base[i];
			base[i] = ch;
		}
		lim = base0 + ((base-base0)/2 - width);
		if (base > base0+width)
			heapify(base0, base0, lim, base-width, width, compar);
	}
}

static void heapify(base0, base, lim, last, width, compar)
	register char	*base0, *base, *lim, *last;
	register long	width;
	register int	(*compar)();
{
	register long	i;
	register char	ch;
	register char	*left_son, *large_son;

	left_son = base0 + 2*(base-base0) + width;
	while (base <= lim) {
		if (left_son == last)
			large_son = left_son;
		else
			large_son = (*compar)(left_son, left_son+width) >= 0 ?
							left_son : left_son+width;
		if ((*compar)(base, large_son) < 0) {
			for (i=0; i<width; ++i) {
				ch = base[i];
				base[i] = large_son[i];
				large_son[i] = ch;
			}
			base = large_son;
			left_son = base0 + 2*(base-base0) + width;
		} else
			break;
	}
}