;;; Copyright (c) 1985 Yale University ;;; Authors: N Adams, R Kelsey, D Kranz, J Philbin, J Rees. ;;; This material was developed by the T Project at the Yale ;;; University Computer Science Department. Permission to copy this ;;; software, to redistribute it, and to use it for any purpose is ;;; granted, subject to the following restric- tions and ;;; understandings. ;;; 1. Any copy made of this software must include this copyright ;;; notice in full. ;;; 2. Users of this software agree to make their best efforts (a) to return ;;; to the T Project at Yale any improvements or extensions that they make, ;;; so that these may be included in future releases; and (b) to inform ;;; the T Project of noteworthy uses of this software. ;;; 3. All materials developed as a consequence of the use of this software ;;; shall duly acknowledge such use, in accordance with the usual standards ;;; of acknowledging credit in academic research. ;;; 4. Yale has made no warrantee or representation that the operation of ;;; this software will be error-free, and Yale is under no obligation to ;;; provide any services, by way of maintenance, update, or otherwise. ;;; 5. In conjunction with products arising from the use of this material, ;;; there shall be no use of the name of the Yale University nor of any ;;; adaptation thereof in any advertising, promotional, or sales literature ;;; without prior written consent from Yale in each case. ;;; ;;; We gratefully acknowledge Bob Nix ;;; SORT:ONLINE-MERGE-SORT! ;;; ======================= ;;; On-Line Merge sort, a fast and stable algorithm for sorting a list. ;;; This is a very neat algorithm! Consider the following code: ;;; ;;; (DEFINE (MERGE-SORT L) ;;; (IF (NULL? (CDR L)) ;;; L ;;; (MERGE (MERGE-SORT (FIRST-HALF-OF L)) ;;; (MERGE-SORT (SECOND-HALF-OF L))))) ;;; ;;; The nested calls to MERGE above form a binary tree, with MERGE's of ;;; singleton lists at the leaves, and a MERGE of two lists of size N/2 at ;;; the top. The algorithm below traverses this MERGE-tree in post-order, ;;; moving from the lower left hand corner to the right. ;;; ;;; This algorithm sorts N objects with about NlgN+2N comparisons and exactly ;;; lgN conses. The algorithm used is a version of mergesort that is ;;; amenable to Lisp's data accessing primitives. The first phase of the ;;; algorithm is an "addition" phase in which each element X is added to ;;; a list of lists of sorted runs B in much the same manner as a one is ;;; added to a binary number. If the first "digit" of B is 0, i.e. the first ;;; list in B is NIL, then the element to be added becomes the first digit ;;; of B. If that digit is non empty then you merge the digit with X and ;;; recurse on the rest of B -- setting the first digit of B to be zero. ;;; For example: ;;; ;;; Reversed LIST B ;;; Binary # Each sublist is sorted. ;;; ;;; 0000 () ;;; 1000 ((x)) ;;; 0100 (() (x x)) ;;; 1100 ((x) (x x)) ;;; 0010 (() () (x x x x)) ;;; 1010 ((x) () (x x x x)) ;;; 0110 (() (x x) (x x x x)) ;;; 1110 ((x) (x x) (x x x x)) ;;; 0001 (() () () (x x x x x x x x)) ;;; 1001 ((x) () () (x x x x x x x x)) ;;; ;;; The algorithm then merges the sublists of these lists into ;;; one list, and returns that list. ;;; ;;; To see the algorithm in action, trace LIST-MERGE!. ;;; ;;; Returns list L sorted using OBJ-< for comparisons. (define (sort-list l obj-<) (cond ((or (null? l) (null? (cdr l))) l) (else (online-merge-sort! (append l '()) ; copy-list obj-<)))) ;;; Returns list L sorted using OBJ-< for comparisons. ;;; L is destructively altered. (define (sort-list! l obj-<) (cond ((or (null? l) (null? (cdr l))) l) (else (online-merge-sort! l obj-<)))) ;;; The real sort procedure. Elements of L are added to B, a list of sorted ;;; lists as defined above. When all elements of L have been added to B ;;; the sublists of B are merged together to get the desired sorted list. (define (online-merge-sort! l obj-<) (let ((b (cons '() '()))) (let loop ((l l)) (cond ((null? l) (do ((c (cddr b) (cdr c)) (r (cadr b) (list-merge! (car c) r obj-<))) ((null? c) r))) (else (let ((new-l (cdr l))) (set-cdr! l '()) (add-to-sorted-lists l b obj-<) (loop new-l))))))) ;;; X is a list that is merged into B, the list of sorted lists. (define (add-to-sorted-lists x b obj-<) (let loop ((x x) (b b)) (let ((l (cdr b))) (cond ((null? l) (set-cdr! b (cons x '()))) ((null? (car l)) (set-car! l x)) (else (let ((y (list-merge! x (car l) obj-<))) (set-car! l '()) (loop y l))))))) ;;; Does a stable side-effecting merge of L1 and L2. (define (list-merge! l1 l2 obj-<) (cond ((null? l1) l2) ((null? l2) l1) ((obj-< (car l1) (car l2)) (real-list-merge! l2 (cdr l1) obj-< l1) l1) (else (real-list-merge! l1 (cdr l2) obj-< l2) l2))) ;;; Does the real work of LIST-MERGE!. L1 is assumed to be non-empty. (define (real-list-merge! l1 l2 obj-< prev) (let loop ((a l1) (b l2) (prev prev)) (cond ((null? b) (set-cdr! prev a)) ((obj-< (car a) (car b)) (set-cdr! prev a) (loop b (cdr a) a)) (else (set-cdr! prev b) (loop a (cdr b) b)))))