# 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);
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.