PARTITIONING A VECTOR

Problem: Given a vector, a ``pivot'' value, and a predicate expressing a total ordering of a type containing the vector elements and the pivot value, rearrange the elements of the vector so that all the vector elements that precede the pivot value in the ordering are in lower-numbered positions than any of the vector elements that do not precede the pivot value, and also determine the lowest-numbered position occupied by an element that does not precede the pivot value. (If all the elements precede the pivot value, return the length of the vector.)

Here is the ``colliding pointers'' partitioning method recommended by Hoare in his description of the quicksort algorithm. The ``pointers'' are initially placed at the left and right ends of the vector. The left one is moved rightwards until it points to an element that does not precede the pivot; the right one is moved leftwards until it points to one that does precede the pivot. Then the two designated elements are swapped, each pointer is moved inwards one additional position, and the process is repeated. Eventually the pointers cross, indicating that the partition is complete.

In the implementation presented here, the ordering predicate is optional and defaults to <.

(define partition!
  (lambda (vec pivot . opt)
    (let ((precedes? (if (null? opt) < (car opt)))
          (len (vector-length vec)))

      (letrec ((rightwards
                (lambda (start)
                  (if (and (< start len)
                           (precedes? (vector-ref vec start) pivot))
                      (rightwards (+ start 1))
                      start)))

               (leftwards
                (lambda (start)
                 (if (or (negative? start)
                         (precedes? (vector-ref vec start) pivot))
                     start
                     (leftwards (- start 1))))))

        (let loop ((left-pointer (rightwards 0))
                   (right-pointer (leftwards (- len 1))))
          (if (< left-pointer right-pointer)
              (begin
                (vector-swap! vec left-pointer right-pointer)
                (loop (rightwards (+ left-pointer 1))
                      (leftwards (- right-pointer 1))))
              left-pointer))))))
(The procedure vector-swap! is defined elsewhere in this collection of algorithms.)

In Programming pearls (Reading, Massachusetts: Addison-Wesley Publishing Company, 1986), page 110, Jon Bentley recommends the following alternative partitioning algorithm, which he ascribes to Nico Lomuto. Bentley argues persuasively that its greater simplicity compensates for its lesser efficiency.

(define partition!
  (lambda (vec pivot . opt)
    (let ((precedes? (if (null? opt) < (car opt)))
          (len (vector-length vec)))
      (do ((index 0 (+ index 1))
           (break 0))
          ((= index len) break)
        (if (precedes? (vector-ref vec index) pivot)
            (begin
              (vector-swap! vec index break)
              (set! break (+ break 1))))))))


This document is available on the World Wide Web as

http://www.math.grin.edu/~stone/events/scheme-workshop/partition.html


created July 12, 1995
last revised June 24, 1996

John David Stone (stone@math.grin.edu)