Fundamentals of Computer Science I: Media Computing (CS151.02 2007F)

Insertion Sort


Summary: In this lab, we explore a variety of issues related to the insertion sort algorithm.

Preparation

a. Copy the code from the end of this lab into your definitions pane or your library. (If you copy it to your library, keep your library open, since you will be editing some of the code.)

b. Copy the list of objects entitled drawing from the end of this lab into your definitions pane.

Exercises

Exercise 1: Testing Insert

a. Test the insert-number procedure from the reading by inserting the number 42

  • into an empty list;
  • into a list of numbers larger than 42, arranged in ascending order;
  • into a list of numbers smaller than 42, arranged in ascending order;
  • into a list of numbers both smaller and larger than 42, arranged in ascending order; and
  • into a list that contains only three copies of 42 (that is, the list created by (list 42 42 42).

b. Discuss with your partner (or someone nearby) why you think we had you do each of these tests. (That is, why would one want to check that insert-number works on each of these lists.)

c. What would you expect to happen if the list is not in ascending order when insert-number is invoked?

d. Check you answer experimentally.

Exercise 2: Inserting Strings

Write a new insert-string procedure that inserts a string into a list of strings that are in alphabetical order:

> (insert-string (list "ape" "bear" "cat" "emu" "frog") "dog")
("ape" "bear" "cat" "dog" "emu" "frog")

In case you've forgotten, string<=? and string-ci<=? are useful predicates for comparing strings for order.

You may not use the generalized insert procedure in writing insert-string.

Exercise 3: Displaying Steps in Insertion Sort

a. Add calls to the display and newline procedures to the body of the helper in numbers.insertion-sort so that it displays the values of unsorted and sorted, appropriately labeled, at each step of the sorting process.

b. Use the revised numbers.insertion-sort procedure to sort the values 7, 6, 12, 4, 10, 8, 5, and 1.

Exercise 4: Checking Potential Problems

When we use a new procedure, we often want to test it on a variety of cases. We've seen that numbers.insertion-sort works on a few simple cases. But we should also check some “special cases”, cases that might stress the algorithm. Make a list of some lists that a poorly-implemented insertion sort procedure might have difficulty with.

Exercise 5: Checking Potential Problems, Revisited

Review the code for numbers.insertion-sort to figure out what you think it will do for each of the following cases. Then check your answer experimentally.

a. An empty list.

b. A list containing only one element.

c. A list containing all equal values.

d. A list in which the elements are originally in descending numerical order.

e. A list in which the elements are already in ascending numerical order.

Exercise 6: Generalized Insertion Sort

a. Write a call to the generalized list.insertion-sort to sort the list ("clementine" "starfruit" "apple" "kumquat" "pineapple" "pomegranate") alphabetically.

b. Write a call to the generalized list.keyed-insertion-sort to sort the same list alphabetically.

c. Review the structure of drawing (a list of named objects). Then write a call to the generalized list.keyed-insertion-sort to sort drawing by object name.

d. Write a call to the generalized list.insertion-sort to sort drawing alphabetically by color name.

e. Write a call to the generalized list.keyed-insertion-sort to sort drawing by object width.

Exercise 7: Observing insert!

a. Add the following definition to your definitions pane.

(define numbers (vector 1 5 6 7 2 8 0 3))

b. Check that vector.insert! works by using it to move the 2 into the correct place in numbers.

Note: Solving this step requires that you understand the parameters to vector.insert!.

c. Extend vector.insert! so that it displays the vector and the position at every step. That is, add calls to display and newline in the kernel, before the cond.

d. Re-create the numbers vector from step a, and observe what happens when we insert the 2, then the 8, then the 0, then the 3.

e. Observe the insertion steps in a vector of about eight randomly-generated numbers.

> (define nums (vector (random 10) (random 10) (random 10) (random 10)
               (random 10) (random 10) (random 10) (random 10)))
> (vector.insertion-sort! nums _____)

f. Explain, in your own words, how this procedure works.

For those with Extra Time

Extra 1: Keyed Insertion Sort for Vectors

Write a procedure, (vector.keyed-insertion-sort vec get-key may-precede?) that sorts a vector of compound objects by key.

While it is possible to write this procedure by converting the vector to a list, using list.keyed-insertion-sort!, and then converting the result back to a vector, you should not use this strategy. Rather, write this procedure directly. (You may want to use vector.insertion-sort! as a template.)

Some Useful Code

Here are some of the more important procedures from the reading.

;;; Procedure:
;;;   insert-number
;;; Parameters:
;;;   sorted, a list of real numbers
;;;   new-element, a real numbers
;;; Purpose:
;;;   Insert new-element into sorted.
;;; Produces:
;;;   new-ls, a new list of real numbers
;;; Preconditions:
;;;   sorted is a list of numbers arranged in increasing order.   That is,
;;;     (<= (list-ref sorted i) (list-ref sorted (+ i 1)))
;;;     for all reasonable values of i.  [Unverified]
;;;   new-element is a number. [Unverified]
;;; Postconditions:
;;;   new-ls is a list of numbers arranged in increasing order.
;;;   new-ls is a permutation of (cons new-element sorted).

(define insert-number
  (lambda (sorted new-element)
    (cond ((null? sorted) 
           (list new-element))
          ((<= new-element (car sorted)) 
           (cons new-element sorted))
          (else 
           (cons (car sorted) 
                 (insert-number (cdr sorted) new-element))))))

;;; Procedure:
;;;   numbers.insertion-sort
;;; Parameters:
;;;   numbers, a list of real numbers
;;; Purpose:
;;;   Sorts numbers
;;; Produces:
;;;   sorted, a list of real numbers
;;; Preconditions:
;;;   (none)
;;; Postconditions:
;;;   sorted is a list of real numbers.
;;;   sorted is organized in increasing order.  That is, 
;;;     (<= (list-ref sorted i) (list-ref sorted (+ i 1)))
;;;     for all reasonable values of i.
;;;   sorted is a permutation of numbers.
(define numbers.insertion-sort
  (lambda (numbers)
    (let kernel ((unsorted numbers)  ; The remaining unsorted values
                 (sorted null))      ; The sorted values
      (if (null? unsorted) 
          sorted
          (kernel (cdr unsorted) (insert-number sorted (car unsorted)))))))

(define list.insertion-sort
  (lambda (lst may-precede?)
    (letrec ((insert
              (lambda (lst val)
                (cond
                  ((null? lst)
                   (list val))
                  ((may-precede? val (car lst))
                   (cons val lst))
                  (else
                   (cons (car lst) (insert (cdr lst) val))))))
             (kernel
              (lambda (unsorted sorted)
                (if (null? unsorted) 
                    sorted
                    (kernel (cdr unsorted) (insert sorted (car unsorted)))))))
      (kernel lst null))))

(define list.keyed-insertion-sort
  (lambda (lst get-key may-precede?)
    (letrec ((insert
              (lambda (lst val)
                (cond
                  ((null? lst)
                   (list val))
                  ((may-precede? (get-key val) (get-key (car lst)))
                   (cons val lst))
                  (else
                   (cons (car lst) (insert (cdr lst) val))))))
             (kernel
              (lambda (unsorted sorted)
                (if (null? unsorted) 
                    sorted
                    (kernel (cdr unsorted) (insert sorted (car unsorted)))))))
      (kernel lst null))))

;;; Procedure:
;;;   vector.insert!
;;; Parameters:
;;;   vec, a vector of values
;;;   new-element, a value
;;;   boundary, an index into the vector
;;;   may-precede?, a binary predicate
;;; Purpose:
;;;   Insert new-element into the portion of vec between 0 and
;;;   boundary, inclusive.
;;; Produces:
;;;   [Nothing; called for side effects.]
;;; Preconditions:
;;;   0 <= boundary < (vector-length vec)
;;;   The elements in positions 0..boundary-1 of vec are sorted.
;;;     That is, (may-precede? (vector-ref vec i) (vector-ref vec (+ i 1)))
;;;     for all 0 <= i < boundary-2.
;;;   may-precede? is transitive and sensible.
;;; Postconditions:
;;;   The elements in positions 0..boundary of vec are sorted.
;;;     That is, (may-precede? (vector-ref vec i) (vector-ref vec (+ i 1)))
;;;     for all 0 <= i < boundary.
;;;   The elements in positions 0..boundary of vec after insert! finishes
;;;     are a permutation of new-element and the elements that were in 
;;;     positions 0..(boundary-1) before the procedure started.
(define vector.insert!
  (lambda (vec new-element boundary may-precede?)
    (let kernel ((pos boundary))
      (cond 
        ; If we've reached the left end of the vector, we've run out of
        ; elements to shift.  Insert the new element.
        ((zero? pos)  
         (vector-set! vec pos new-element))
        ; If we've reached a point at which the element to the left
        ; is smaller, we insert the new element here.
        ((may-precede? (vector-ref vec (- pos 1)) new-element)
         (vector-set! vec pos new-element))
        ; Otherwise, we shift the current element to the right and
        ; continue.
        (else
         (vector-set! vec pos (vector-ref vec (- pos 1)))
         (kernel (- pos 1)))))))

;;; Procedure:
;;;   vector.insertion-sort!
;;; Parameters:
;;;   vec, a vector 
;;;   may-precede?, a binary predicate
;;; Purpose:
;;;   Sorts the vector.
;;; Produces:
;;;   [Nothing; sorts in place]
;;; Preconditions:
;;;   vec is a vector.
;;;   may-precede? can be applied to any two elements of vec.
;;;   may-precede? is transitive.
;;; Postconditions:
;;;   The final state of vec is a permutation of the original state.
;;;   vec is sorted.  That is, 
;;;     (may-precede? (vector-ref vec i) (vector-ref vec (+ i 1)))
;;;     for all reasonable values of i.
(define vector.insertion-sort!
  (lambda (vec may-precede?)
    (let ((len (vector-length vec)))
      (let kernel ((boundary 1)) ; The index of the first unsorted value
        (cond
          ((< boundary len) ; If we have elements left to sort
           (vector.insert! vec 
                           (vector-ref vec boundary) 
                           boundary
                           may-precede?)
           (kernel (+ boundary 1)))
          (else
           vec))))))

You will also find the following definition useful.

(define drawing
  (list (list "circ1" "ellipse" "red" 10 10 80 80)
        (list "thin" "ellipse" "blue" 10 80 300 10)
        (list "tall" "rectangle" "green" 80 5 100 2)
        (list "ys1" "rectangle" "yellow" 0 50 10 10)
        (list "ys2" "rectangle" "yellow" 0 50 20 20)
        (list "ys3" "rectangle" "yellow" 0 55 30 30)
        (list "ys4" "rectangle" "yellow" 0 60 40 40)
        (list "ys5" "rectangle" "yellow" 0 65 50 50)
        (list "ys6" "rectangle" "yellow" 0 70 60 60)
        (list "rc" "ellipse" "red" 100 100 30 30)
        (list "oc" "ellipse" "orange" 90 110 30 30)
        (list "yc" "ellipse" "yellow" 80 120 30 30)
        (list "gc" "ellipse" "green" 80 130 30 30)
        (list "bc" "ellipse" "blue" 90 140 30 30)
        (list "ic" "ellipse" "indigo" 100 150 30 30)
        (list "vc" "ellipse" "violet" 110 160 30 30)
        (list "last" "rectangle" "white" 0 0 1 1)))

Creative Commons License

Samuel A. Rebelsky, rebelsky@grinnell.edu

Copyright 2007 Janet Davis, Matthew Kluber, and Samuel A. Rebelsky. (Selected materials copyright by John David Stone and Henry Walker and used by permission.)

This material is based upon work partially supported by the National Science Foundation under Grant No. CCLI-0633090. Any opinions, findings, and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the National Science Foundation.

This work is licensed under a Creative Commons Attribution-NonCommercial 2.5 License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/2.5/ or send a letter to Creative Commons, 543 Howard Street, 5th Floor, San Francisco, California, 94105, USA.