CMPSCI 187: Programming With Data Structures

David Mix Barrington

Fall, 2011

Discussion Assignment #7: Ordered Lists With Holes

26 October 2011

If we implement an ordered list as an array, inserting a new element may require shifting a large fraction of the old elements, in order to create a "hole" for the new element in the right place. In this discussion we're going to look at a scheme to prevent or defer some of this work, by prepositioning holes in the array so that new elements can fill them.

We are going to define a new generic class HoleyList<T>, which will implement L&C's interface OrderedListADT<T> (though we won't deal with all the required methods here). An object of type HoleyList<T> will store elements of type T in an array of type T [ ], but the array will contain both elements and null entries called "empty slots". The empty slots may occur between real entries. The class also has a field int size for the number of real elements being stored.

Given an array of length n, full of T elements, we construct a HoleyList by creating an array of length 2n, then placing the elements in the even-numbered positions -- the first in entry 0, the second in entry 2, and so on, so that the last real element goes in entry 2n - 2 and entry 2n - 1 (like all other odd-numbered entries) is null.

Removing an element from a HoleyList is simple -- we replace its entry with null and don't move any other elements or otherwise change the array at all. The add operation is more interesting. To add a new element y, we first find the largest element x that is less than y in the natural order. (There is a special case if there is no such element, but it is quite similar.) We want to put y in the slot immediately after x:

  1. If the slot immediately after x is empty, we just put y there and we are done.

  2. If that slot is full, we find the next empty slot in the list, if any. We shift the elements between x and that slot each forward by one entry, making an empty slot right after x into which we put y. Note that the array is not circular -- if we reach the end without finding an empty slot, we give up.

  3. If there is no empty slot after x at all, we resize the array. This means we make a new array twice as big as the number of elements we are storing, and copy all the real entries of the old array, in order, into the even-numbered entries of the new one. Thus the new array is exactly half full -- it is not necessarily twice the size of the old array. We then insert y into the new empty slot immediately after x.

    Exercises:

    Question 1: Trace the following code given the description of the HoleyList class above. Remember that these entries are String objects, not Dog objects, and that the natural order on String objects is just ordinary dictionary order. Describe the entire array after each operation.

    
        String [ ] someDogs = {"Ace", "Biscuit", "Cardie"};
        HoleyList<String> holey = new HoleyList<String>(someDogs);
        holey.add ("Balto");
        holey.add ("Buck");
        holey.add ("Bella");
        holey.remove ("Ace");
        holey.add ("Duncan");
        holey.add ("Abigail");
        holey.add ("Aaron");
    

    Question 2: Write a constructor public HoleyList (T [ ] input) for HoleyList. You may (or may not) find it convenient to write a method copyToEvens (T [ ] oldArray, T [ ] newArray) that you can also use later. For the constructor, you may assume that the input array is in the natural order and contains no empty slots.

    Question 3: Write the add and remove methods for HoleyList. You may find it convenient to write a find method first. Remember that remove takes a T parameter. It throws an EmptyCollectionException if the list is empty, and an ElementNotFoundException if the parameter is not equal to any element in the list.

    Question 4: In our array implementations of stacks, queues, and deques, the add operation "took O(1) time, except for resizing." But the add operation of an ArrayList, in either L&C's version or the library version, takes O(n) time in the worst case even without resizing. Is the add operation of our new class "O(1) in the worst case, except for resizing"? Justify your answer.

    Last modified 27 October 2011