Comp 271 Week 8

Midterm: Tuesday, October 20.
You will be given a simplified ArrayList class, to serve as examples of various things.

Demo: create a linkedlist and see it in action with the inspector. Note that the head cell is dataless!
Separate demo: iterator + concurrent list modification = kablooey!
Can you make your iterator do that?
Also note I have List<E> li = new LinkedList<E>()

The data for an ArrayList iterator "is" a position-marker in the array (sometimes called a cursor). The key data for a LinkedList iterator is a pointer to a specific cell. The respective next() operations are cursor+=1 and p=p.next(), in addition to returning the current array[cursor] / p.data(). The respective hasNexts are cursor<array.length and p!=null.

Stacks and Queues

Bailey has these both implement Linear<E>. I think this is misleading: stacks and queues are supposed to be very spare, and not have extra methods. This way, you have class invariants that are preserved; eg the only way you can access an element in the middle of the stack is to pop (and thus remove) everything above it.

Basic stack operations: push, pop, empty

Bailey adds add(), same as pop(), and remove, same as push(), in order to meet the linear interface. Also peek(), which is not necessary but often convenient.

Stacks are LIFO.

classic lunch-tray model v picture on page 225

Stacks can be implemented as arraylists, or anything else. See java.util.Stack.

Stacks used to check parenthesis balance
    Basic logic:
          valid -> empty
          valid -> valid valid
          valid -> ( valid )

When we add [] and {}, we add
          valid -> { valid }
          valid -> [ valid ]

Writing a recursive balance-checker is moderately straightforward. However, it helps to have a globally visible nextChar that represents the next symbol.

Recursion simulation with a stack

Linear examples: kind of trivial: we just push a sequence of values!
Quicksort example starting on page 222

Ultimately, recursion is implemented using a stack.

Queues

FIFO: add() and remove() are now most commonly known as enqueue() and dequeue(). But note the semantics differ! Usually, queues are FIFO. Also, note that a direct implementation is just a wee easier with, say, doubly-linked lists; with an array, the data creeps along. Or else you do performance-robbing inserts or deletes at the front of the array. The classic implementation of a queue using an arraylist and add/delete is O(n2) for n inserts/deletes!

Suppose we want to implement a queue using an array A that goes from 0 to MAX-1. We have two positions, head and tail.
When head==tail, the queue is empty. No elements will ever be shifted. When we reach tail=MAX, we reset tail=0, and continue inserting there; ditto for head.
    public void enqueue(T val) {
       if (isfull() ) return;
       A[tail]=val;                // tail is position of next open slot
       tail++;
       if (tail==MAX) tail=0;
    }

    public T dequeue() {
       if (head==tail) return;        // queue is empty
       T val = A[head];             // head is position of next elt to be dequeued
       head++;
       if (head==MAX) head=0;
       return val;
    }

There is one minor trickiness with determining whether the queue is full: if we fill every cell, then head==tail again and we can't tell if the queue is full or empty! So we will always leave one slot vacant, and have the queue be full if head=tail+1, mod MAX (ie if head==tail+1 or head==0 and tail+1==MAX.

Note that java.util.Queue() is an interface. One reason is because, in the real world, there are lots of non-FIFO queues. A queue is really a place for things to wait, and waiting occurs only when you have concurrent programs. So there aren't a lot of simple problems solved with queues.

Thursday

Study guide

Review array-based queues

Queues example:
    Coins puzzle
    Finding the shortest way out of a maze
    Stack version of maze puzzle

Sorted Lists and Binary Search


Binary array search: example code from my demo:

    public int search(double x) {
int lo = 0, hi = A.length-1; /*# we know that either A[i]=x and lo<=i<=hi, or else x is not found.*/
int count = 0;
do {
count++;
int mid = (lo+hi)/2; // lo <=mid<hi;
if (A[mid] == x) {
System.out.println("number of iterations: " + count);
return mid;
}
if (A[mid]<x) lo = mid+1; // upper half; lo <=hi
else hi = mid-1; // could result in hi = lo-1!
} while (lo < hi);
if (lo==hi && A[lo]==x) return lo;
System.out.println("number of iterations: " + count);
return -1;
}
Note the use of assertions and invariants to help us figure out when to use < and when to use <=.

Here's where things stand in terms of finding an element and inserting a new element, assuming binary search is used in the array version:

ArrayList
LinkedList
search
O(log n)
O(n)
insert
O(n)
O(1)

Our goal after the exam is to introduce tree structures that combine the best of both worlds.

Slightly simpler version of binary search with compareTo:
    public int search(String x) {
int lo = 0, hi = A.size()-1; /*# we know that either A[i]=x and lo<=i<=hi, or else x is not found.*/
int count = 0;
do {
count++;
int mid = (lo+hi)/2; // lo <=mid<hi;
int res = x.compareTo(A.get(mid));
if (res== 0) { // found
System.out.println("number of iterations: " + count);
return mid;
}
else if (res > 0) lo = mid+1; // upper half; lo <=hi
else hi = mid-1; // could result in hi = lo-1!
} while (lo < hi);
if (lo==hi && A.get(lo).equals(x)) return lo;
System.out.println("number of iterations: " + count);
return -1;
}

The OrderedStructure interface, p 259

(This is Bailey's; there is no java.util version)

In my version of lab5 (labs/lab5/pldlist on my computer), there is something that can be made into a sorted-list class. However, all the mutators that allow non-sorted insertion would have to be removed, and insertInOrder() would be renamed simply insert() (or add()).

Array version: finding is O(log(n)); inserting is O(n).
Linked version: finding is O(n); inserting is O(1).

Can we do better?