xref: /original-bsd/lib/libc/stdlib/heapsort.c (revision c3e32dec)
1 /*-
2  * Copyright (c) 1991, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Ronnie Kon at Mindcraft Inc., Kevin Lew and Elmer Yglesias.
7  *
8  * %sccs.include.redist.c%
9  */
10 
11 #if defined(LIBC_SCCS) && !defined(lint)
12 static char sccsid[] = "@(#)heapsort.c	8.1 (Berkeley) 06/04/93";
13 #endif /* LIBC_SCCS and not lint */
14 
15 #include <sys/types.h>
16 #include <errno.h>
17 #include <stdlib.h>
18 #include <stddef.h>
19 
20 /*
21  * Swap two areas of size number of bytes.  Although qsort(3) permits random
22  * blocks of memory to be sorted, sorting pointers is almost certainly the
23  * common case (and, were it not, could easily be made so).  Regardless, it
24  * isn't worth optimizing; the SWAP's get sped up by the cache, and pointer
25  * arithmetic gets lost in the time required for comparison function calls.
26  */
27 #define	SWAP(a, b, count, size, tmp) { \
28 	count = size; \
29 	do { \
30 		tmp = *a; \
31 		*a++ = *b; \
32 		*b++ = tmp; \
33 	} while (--count); \
34 }
35 
36 /* Copy one block of size size to another. */
37 #define COPY(a, b, count, size, tmp1, tmp2) { \
38 	count = size; \
39 	tmp1 = a; \
40 	tmp2 = b; \
41 	do { \
42 		*tmp1++ = *tmp2++; \
43 	} while (--count); \
44 }
45 
46 /*
47  * Build the list into a heap, where a heap is defined such that for
48  * the records K1 ... KN, Kj/2 >= Kj for 1 <= j/2 <= j <= N.
49  *
50  * There two cases.  If j == nmemb, select largest of Ki and Kj.  If
51  * j < nmemb, select largest of Ki, Kj and Kj+1.
52  */
53 #define CREATE(initval, nmemb, par_i, child_i, par, child, size, count, tmp) { \
54 	for (par_i = initval; (child_i = par_i * 2) <= nmemb; \
55 	    par_i = child_i) { \
56 		child = base + child_i * size; \
57 		if (child_i < nmemb && compar(child, child + size) < 0) { \
58 			child += size; \
59 			++child_i; \
60 		} \
61 		par = base + par_i * size; \
62 		if (compar(child, par) <= 0) \
63 			break; \
64 		SWAP(par, child, count, size, tmp); \
65 	} \
66 }
67 
68 /*
69  * Select the top of the heap and 'heapify'.  Since by far the most expensive
70  * action is the call to the compar function, a considerable optimization
71  * in the average case can be achieved due to the fact that k, the displaced
72  * elememt, is ususally quite small, so it would be preferable to first
73  * heapify, always maintaining the invariant that the larger child is copied
74  * over its parent's record.
75  *
76  * Then, starting from the *bottom* of the heap, finding k's correct place,
77  * again maintianing the invariant.  As a result of the invariant no element
78  * is 'lost' when k is assigned its correct place in the heap.
79  *
80  * The time savings from this optimization are on the order of 15-20% for the
81  * average case. See Knuth, Vol. 3, page 158, problem 18.
82  *
83  * XXX Don't break the #define SELECT line, below.  Reiser cpp gets upset.
84  */
85 #define SELECT(par_i, child_i, nmemb, par, child, size, k, count, tmp1, tmp2) { \
86 	for (par_i = 1; (child_i = par_i * 2) <= nmemb; par_i = child_i) { \
87 		child = base + child_i * size; \
88 		if (child_i < nmemb && compar(child, child + size) < 0) { \
89 			child += size; \
90 			++child_i; \
91 		} \
92 		par = base + par_i * size; \
93 		COPY(par, child, count, size, tmp1, tmp2); \
94 	} \
95 	for (;;) { \
96 		child_i = par_i; \
97 		par_i = child_i / 2; \
98 		child = base + child_i * size; \
99 		par = base + par_i * size; \
100 		if (child_i == 1 || compar(k, par) < 0) { \
101 			COPY(child, k, count, size, tmp1, tmp2); \
102 			break; \
103 		} \
104 		COPY(child, par, count, size, tmp1, tmp2); \
105 	} \
106 }
107 
108 /*
109  * Heapsort -- Knuth, Vol. 3, page 145.  Runs in O (N lg N), both average
110  * and worst.  While heapsort is faster than the worst case of quicksort,
111  * the BSD quicksort does median selection so that the chance of finding
112  * a data set that will trigger the worst case is nonexistent.  Heapsort's
113  * only advantage over quicksort is that it requires little additional memory.
114  */
115 int
116 heapsort(vbase, nmemb, size, compar)
117 	void *vbase;
118 	size_t nmemb, size;
119 	int (*compar) __P((const void *, const void *));
120 {
121 	register int cnt, i, j, l;
122 	register char tmp, *tmp1, *tmp2;
123 	char *base, *k, *p, *t;
124 
125 	if (nmemb <= 1)
126 		return (0);
127 
128 	if (!size) {
129 		errno = EINVAL;
130 		return (-1);
131 	}
132 
133 	if ((k = malloc(size)) == NULL)
134 		return (-1);
135 
136 	/*
137 	 * Items are numbered from 1 to nmemb, so offset from size bytes
138 	 * below the starting address.
139 	 */
140 	base = (char *)vbase - size;
141 
142 	for (l = nmemb / 2 + 1; --l;)
143 		CREATE(l, nmemb, i, j, t, p, size, cnt, tmp);
144 
145 	/*
146 	 * For each element of the heap, save the largest element into its
147 	 * final slot, save the displaced element (k), then recreate the
148 	 * heap.
149 	 */
150 	while (nmemb > 1) {
151 		COPY(k, base + nmemb * size, cnt, size, tmp1, tmp2);
152 		COPY(base + nmemb * size, base + size, cnt, size, tmp1, tmp2);
153 		--nmemb;
154 		SELECT(i, j, nmemb, t, p, size, k, cnt, tmp1, tmp2);
155 	}
156 	free(k);
157 	return (0);
158 }
159