/* quicksort and select as taken from CLR. -bds 9/95 */ #include "sort.h" /* sort.h defines types Key, Record, Entry = struct{Key key, Record r}, and procedure swap. and comparison operators on type Key (>, <=, <, >=, ==, !=). */ int Partition(Entry* A, int p, int r, Key x) /* Assumes p <= r are valid indices for A, and x = A[i] for some i, p <= i <= r. Returns index q, p <= q < r and permutes A so that A[p] thru A[q] are <= x and A[q+1] thru A[r] are >= x. where x is the original A[p]. */{ int i = p; int j = r; while (1) { while (A[j].key > x) j--; while (A[i].key < x) i++; if (i < j) { swap(A, i, j); // Now A[i] <= x <= A[j], i++; j--; } else return j < r ? j : j-1j; } } int Quick_Partition(Entry* A, int p, int r) /* Assumes p <= r are valid indices for A. Returns index q, p <= q < r and permutes A so that A[p] thru A[q] are <= x and A[q+1] thru A[r] are >= x. where x is the original A[p]. */{ key x = A[p].key; Partition(A, p, r, x); } void Quicksort(Entry* A, int p, int r) /* Assumes p <= r are valid indices for A. Permutes A so that A[p] thru A[r] are in increasing order. */{ if (p < r) { int q = Quick_Partition(A, p, r); Quicksort(A, p, q); Quicksort(A, q+1, r); } } int Randomized_Partition(Entry* A, int p, int r) /* Assumes p <= r are valid indices for A. Returns index q, p <= q < r and permutes A so that A[p] thru A[q] are <= x and A[q+1] thru A[r] are >= x, where x is a random A[i], p <= i <= r. */{ int i = Random(p,r); return Partition(A, p, r, A[i].key); } void Randomized_Quicksort(Entry* A, int p, int r) /* Assumes p <= r are valid indices for A. Permutes A so that A[p] thru A[r] are in increasing order. */{ if (p < r) { int q = Randomized_Partition(A, p, r); Randomized_Quicksort(A, p, q); Randomized_Quicksort(A, q+1, r); } } Entry Randomized_Select(Entry* A, int p, int r, int i) /* Assumes 1 <= i <= r - p + 1 and Returns the i-th smallest element of A[p] thru A[r], i.e. returns A'[i], where A' denotes what A would be if A[p] thru A[r] were sorted into increasing order. As a side effect, A is permuted (but not completely sorted). */{ if (p >= r) return A[p]; else { int q = Randomized_Partition(A, p, r); int k = q - p + 1; // k = size of lower partition. if (i <= k) return Randomized_Select(A, p, q, i); else return Randomized_Select(A, q+1, r, i - k); } } Entry Slick_Select(Entry* A, int p, int r, int i); int Slick_Partition(Entry* A, int p, int r) /* Assumes p <= r are valid indices for A. Returns index q, p <= q < r and permutes A so that A[p] thru A[q] are <= x and A[q+1] thru A[r] are >= x. where x is A[i] and p <= i <= r. It is guaranteed that partition is relatively even, to be precise, for large n = r - p + 1, the sizes q - p + 1 and r - q + 1 of the two partitions are both >= 3n/10 - 3. As a side effect, A is permuted (but not completely sorted). */{ Entry* B; int n = r - p + 1; int k = n/5; // floor. B = malloc( (k+1)*sizeof(Entry) );/* could avoid using extra space like this */ for (int i = 1, i <= k; i += 5) { Anysort(A, i, i+4); B[i] = A[i+2]; } Entry x = Slick_Select(B, 1, k, k/2); return Partition(A, p, r, x.key); } void Slick_Quicksort(Entry* A, int p, int r) /* Assumes p <= r are valid indices for A. Permutes A so that A[p] thru A[r] are in increasing order. */{ if (p < r) { int q = Slick_Partition(A, p, r, A[p]); Slick_Quicksort(A, p, q); Slick_Quicksort(A, q+1, r); } Entry Slick_Select(Entry* A, int p, int r, int i) /* Assumes 1 <= i <= r - p + 1 and Returns the i-th smallest element of A[p] thru A[r], i.e. returns A'[i], where A' denotes what A would be if A[p] thru A[r] were sorted into increasing order. As a side effect, A is permuted (but not completely sorted). */{ if (p >= r) return A[p]; else { int q = Slick_Partition(A, p, r), ; int k = q - p + 1; // k = size of lower partition. if (i <= k) return Slick_Select(A, p, q, i); else return Slick_Select(A, q+1, r, i - k); } } /* Theorem 1: Slick_Partition delivers on it's claim: the partition sizes are both >= 3n/10 - 3. Proof: Entries less than x are three in about half the groups of 5. Theorem 2: for n = r - p + 1, Slick_Select(A, p, q, i) and Slick_Partition(A, p, q, x) both run in theta(n) time. Proof: Let S(n) = time for Slick_Select and let P(n) = time for Slick_Partition. Then by examining the code we see the recurrences: S(n) <= P(n) + S(7n/10 + 4) + C, for some constant C P(n) = theta(n) + S(n/5). Let D = 20*C and show by induction that S(n) <= D*n. */