Replace quick_sort() that uses quick sort and O(lg n) space
authorBen Pfaff <blp@cs.stanford.edu>
Wed, 30 Mar 2005 05:17:28 +0000 (05:17 +0000)
committerBen Pfaff <blp@cs.stanford.edu>
Wed, 30 Mar 2005 05:17:28 +0000 (05:17 +0000)
by sort() that uses heap sort and O(1) space.

TODO
grading/vm/page-merge-par.c
grading/vm/page-merge-seq.c
src/lib/stdlib.c
src/lib/stdlib.h

diff --git a/TODO b/TODO
index 3ccce4bb9daeb7a03dfcbbbac464261910378dc6..609063502e19a832027b2dea13e10f2d9633914b 100644 (file)
--- a/TODO
+++ b/TODO
 
 * Code:
 
-  - Rewrite quick_sort() to use heap sort, for O(1) stack usage.
-
   - Make printf() test actually check its results.
 
   - Make threads test use a program and arguments like the other
index 06209e8359b1e6cd9a251964dbee91338b6e0b98..8f3be48cbb64816ab0860f22de9b19a5baaa2935 100644 (file)
@@ -35,7 +35,7 @@ init (void)
 
 /* Sort each chunk of buf1 using a subprocess. */
 static void
-sort (void)
+sort_chunks (void)
 {
   pid_t children[CHUNK_CNT];
   size_t i;
@@ -162,7 +162,7 @@ main (void)
 {
   printf ("(page-merge-par) begin\n");
   init ();
-  sort ();
+  sort_chunks ();
   merge ();
   verify ();
   printf ("(page-merge-par) end\n");
index 203ca9cd25ac42b6ad34da57c3983eaf3c851365..57cd53c6f6531e8e5c33919af6ee0d1964853ceb 100644 (file)
@@ -35,7 +35,7 @@ init (void)
 
 /* Sort each chunk of buf1 using a subprocess. */
 static void
-sort (void)
+sort_chunks (void)
 {
   size_t i;
 
@@ -150,7 +150,7 @@ main (void)
 {
   printf ("(page-merge-seq) begin\n");
   init ();
-  sort ();
+  sort_chunks ();
   merge ();
   verify ();
   printf ("(page-merge-seq) end\n");
index 0056fc1775262dd911895a1324b8b8c99a1e039c..84c7f616716e2a71bce250c9dad86b966a5ee012 100644 (file)
@@ -52,18 +52,22 @@ compare_thunk (const void *a, const void *b, void *aux)
    using COMPARE.  When COMPARE is passed a pair of elements A
    and B, respectively, it must return a strcmp()-type result,
    i.e. less than zero if A < B, zero if A == B, greater than
-   zero if A > B. */
+   zero if A > B.  Runs in O(n lg n) time and O(1) space in
+   CNT. */
 void
 qsort (void *array, size_t cnt, size_t size,
        int (*compare) (const void *, const void *)) 
 {
-  quick_sort (array, cnt, size, compare_thunk, &compare);
+  sort (array, cnt, size, compare_thunk, &compare);
 }
 
-/* Swaps the SIZE bytes at A and B. */
+/* Swaps elements with 1-based indexes A_IDX and B_IDX in ARRAY
+   with elements of SIZE bytes each. */
 static void
-swap (unsigned char *a, unsigned char *b, size_t size) 
+do_swap (unsigned char *array, size_t a_idx, size_t b_idx, size_t size)
 {
+  unsigned char *a = array + (a_idx - 1) * size;
+  unsigned char *b = array + (b_idx - 1) * size;
   size_t i;
 
   for (i = 0; i < size; i++)
@@ -74,47 +78,48 @@ swap (unsigned char *a, unsigned char *b, size_t size)
     }
 }
 
-/* Selects a random "pivot" element in ARRAY, which contains CNT
-   elements of SIZE bytes each, and partitions ARRAY into two
-   contiguous subranges around the pivot, so that the first N
-   elements are less than or equal to the pivot and the remaining
-   elements are greater than the pivot.  Returns N.
+/* Compares elements with 1-based indexes A_IDX and B_IDX in
+   ARRAY with elements of SIZE bytes each, using COMPARE to
+   compare elements, passing AUX as auxiliary data, and returns a
+   strcmp()-type result. */
+static int
+do_compare (unsigned char *array, size_t a_idx, size_t b_idx, size_t size,
+            int (*compare) (const void *, const void *, void *aux),
+            void *aux) 
+{
+  return compare (array + (a_idx - 1) * size, array + (b_idx - 1) * size, aux);
+}
 
-   Uses COMPARE to compare elements, passing AUX as auxiliary
-   data.  When COMPARE is passed a pair of elements A and B,
-   respectively, it must return a strcmp()-type result, i.e. less
-   than zero if A < B, zero if A == B, greater than zero if A >
-   B. */
-static size_t
-randomized_partition (void *array, size_t cnt, size_t size,
-                      int (*compare) (const void *, const void *, void *aux),
-                      void *aux) 
+/* "Float down" the element with 1-based index I in ARRAY of CNT
+   elements of SIZE bytes each, using COMPARE to compare
+   elements, passing AUX as auxiliary data. */
+static void
+heapify (unsigned char *array, size_t i, size_t cnt, size_t size,
+         int (*compare) (const void *, const void *, void *aux),
+         void *aux) 
 {
-  unsigned char *first = array;                 /* Beginning of array. */
-  unsigned char *last = first + size * cnt;     /* End of array. */
-  unsigned char *pivot = last - size;           /* Element used as pivot. */
-  unsigned char *middle;        /* Invariant: elements on left are <= pivot. */
-  unsigned char *current;
-
-  ASSERT (array != NULL);
-  ASSERT (cnt > 1);
-  ASSERT (size > 0);
-  ASSERT (compare != NULL);
-  
-  /* Choose a random pivot. */
-  swap (pivot, first + (random_ulong () % cnt) * size, size);
-
-  /* Iterate through the array moving elements less than or equal
-     to the pivot to the middle. */
-  middle = array;
-  for (current = first; current < last; current += size)
-    if (compare (current, pivot, aux) <= 0) 
-      {
-        swap (middle, current, size);
-        middle += size; 
-      }
-  
-  return (middle - first) / size;
+  for (;;) 
+    {
+      /* Set `max' to the index of the largest element among I
+         and its children (if any). */
+      size_t left = 2 * i;
+      size_t right = 2 * i + 1;
+      size_t max = i;
+      if (left <= cnt && do_compare (array, left, max, size, compare, aux) > 0)
+        max = left;
+      if (right <= cnt
+          && do_compare (array, right, max, size, compare, aux) > 0) 
+        max = right;
+
+      /* If the maximum value is already in element I, we're
+         done. */
+      if (max == i)
+        break;
+
+      /* Swap and continue down the heap. */
+      do_swap (array, i, max, size);
+      i = max;
+    }
 }
 
 /* Sorts ARRAY, which contains CNT elements of SIZE bytes each,
@@ -122,34 +127,27 @@ randomized_partition (void *array, size_t cnt, size_t size,
    data.  When COMPARE is passed a pair of elements A and B,
    respectively, it must return a strcmp()-type result, i.e. less
    than zero if A < B, zero if A == B, greater than zero if A >
-   B. */
+   B.  Runs in O(n lg n) time and O(1) space in CNT. */
 void
-quick_sort (void *array_, size_t cnt, size_t size,
-            int (*compare) (const void *, const void *, void *aux),
-            void *aux) 
+sort (void *array, size_t cnt, size_t size,
+      int (*compare) (const void *, const void *, void *aux),
+      void *aux) 
 {
-  unsigned char *array = array_;
+  size_t i;
 
   ASSERT (array != NULL || cnt == 0);
   ASSERT (compare != NULL);
   ASSERT (size > 0);
-  
-  if (cnt > 1) 
+
+  /* Build a heap. */
+  for (i = cnt / 2; i > 0; i--)
+    heapify (array, i, cnt, size, compare, aux);
+
+  /* Sort the heap. */
+  for (i = cnt; i > 1; i--) 
     {
-      size_t q = randomized_partition (array, cnt, size, compare, aux);
-
-      /* Sort smaller partition first to guarantee O(lg n) stack
-         depth limit. */
-      if (q < cnt - q) 
-        {
-          quick_sort (array, q, size, compare, aux);
-          quick_sort (array + q * size, cnt - q, size, compare, aux);
-        }
-      else 
-        {
-          quick_sort (array + q * size, cnt - q, size, compare, aux);
-          quick_sort (array, q, size, compare, aux);
-        }
+      do_swap (array, 1, i, size);
+      heapify (array, 1, i - 1, size, compare, aux); 
     }
 }
 
index cfc12edeee846d4613941e5b89faaec54f7cbb6c..d14afa38384e599969135d7178342a0624f47e2a 100644 (file)
@@ -11,9 +11,9 @@ void *bsearch (const void *key, const void *array, size_t cnt,
                size_t size, int (*compare) (const void *, const void *));
 
 /* Nonstandard functions. */
-void quick_sort (void *array, size_t cnt, size_t size,
-                 int (*compare) (const void *, const void *, void *aux),
-                 void *aux);
+void sort (void *array, size_t cnt, size_t size,
+           int (*compare) (const void *, const void *, void *aux),
+           void *aux);
 void *binary_search (const void *key, const void *array, size_t cnt,
                      size_t size,
                      int (*compare) (const void *, const void *, void *aux),