1 Iterators and Deletion
1.1 Class Assessments
1.2 First a small puzzle
1.3 Deleting Arbitrary Elements in BST
1.3.1 Hibbard Deletion technique
1.4 In Order Traversal
1.5 Iterators
1.5.1 Stack Iterator
1.5.2 Bag Iterator
1.5.3 Max PQ
1.5.4 Symbol Table
1.6 HW4 Expression Binary Tree
1.7 Version : 2015/ 11/ 25

CS 2223 Nov 24 2015

Lecture Path: 17
Back Next

Expected reading: pp. 412-413
Daily Exercise:

Out, damn’d spot! out, I say! – One; two: why, then ’tis time to do’t –
Hell is murky
Lady Macbeth

1 Iterators and Deletion

1.1 Class Assessments

As announced in class (and revised via my.wpi.edu announcement and email) I am replacing the final HW6 with three in-class quizzes, which will commence each of the subsequent Tuesday’s remaining in this course:

1.2 First a small puzzle

Have you ever been asked one of these MatchStick puzzles? Given the following image, move just one matchstick to create five squares of equal size:

Discussion ensues.

1.3 Deleting Arbitrary Elements in BST

The goal is to delete a key from a BST with the least amount of effort. Doing so is like the matchstick problem.

Now we face the challenging issue of deleting elements anywhere in the BST.

Just to set the stage, construct BST after adding following values in this order:

H, W, D, L, Z, E, A

With this BST in hand, propose a new BST that could result if you were to delete the key H:

Discussion ensues.

1.3.1 Hibbard Deletion technique

The deletion of a Node with two children requires special handling. Fortunately, with the intuition from above, we can see how this might be implemented. Knowing what is supposed to happen is just as important as understanding how this compact code works.

public void delete(Key key) { root = delete(root, key); } private Node delete(Node parent, Key key) { if (parent == null) return null; // recurse until you find node with this key. int cmp = key.compareTo(parent.key); if (cmp < 0) parent.left = delete(parent.left, key); else if (cmp > 0) parent.right = delete(parent.right, key); else { // handle easy cases first: if (parent.right == null) return parent.left; if (parent.left == null) return parent.right; // has two children: Plan on returning min of our right child Node old = parent; parent = min(old.right); // Will eventually be "new parent" // Note this is a simpler case: Delete min from right subtree // and DON’T FORGET to stitch back in the original left child parent.right = deleteMin(old.right); parent.left = old.left; } // as recursions unwind, update size appropriately parent.N = size(parent.left) + size(parent.right) + 1; return parent; }

The key to understanding deletion is breaking the problem into smaller subproblems; in this case, reducing it to the simpler deleteMin case we already covered.

If you never recursively locate a node with the sought for value, then you will eventually bottom out the recursion. Note that as the recursions unwind, the size will be updated, even though nothing was deleted.

However, should a node with the desired key be identified, the two easy cases where that node has a single child are handled immediately. But if there are two children, we know that we want to replace parent node with the smallest node in its right subtree. We thus locate this minimum and then in just a few statements we only need to move a single node to reestablish the Binary Search Tree property. Note that the unwinding of the recursion properly ensures that the N attribute is set correctly.

1.4 InOrder Traversal

What if you wanted to demonstrate a traversal that visited all keys in the BST in order? This is the reason that BSTs are so powerful. Let’s make a small change to the traversal algorithm:

public void inorder() { inorder(root); } private void inorder(Node n) { if (n != null) { inorder (n.left); StdOut.println (n.key); inorder (n.right); } }

The order of the statements is critical! Revisit the earlier BST and try an inorder traversal.

1.5 Iterators

Prior to the midterm, I stayed away from describing the different Iterators as outlined in the book since I wanted to reduce the amount of material you needed to know for the midterm. Now that milestone is past, we have to cover Iterators.

Iterable Collections were first introduced on p. 123 and was presented as a means of processing each of the items in a collection. Using Iterators, one can write clear and compact code that frees the client from having to know about the underlying representation of the data types that it uses. This is an important software engineering issue and it appears here under the guise of designing APIs properly.

In Java, you know that an Iterator is being used when the enhanced for loop is written. This concept would appear in code as follows:

Bag<Double> numbers = new Bag<Double>(); numbers.add(17); numbers.add(13); numbers.add(19); for (Double d : numbers) { StdOut.println(d); }

1.5.1 Stack Iterator

The first Iterator presented is the Stack Iterator, which outputs the values in the stack in reverse order, with the clear intention of explaining to the client the order in which the values would be popped.

On Day4 (Nov 02 2015) I presented the ResizingArrayStack example which contained an iterator, but I didn’t present it in class. Here is the basic structure that you need to know:

package algs.days.day04; public class ResizingArrayStack<Item> implements Iterable<Item> { /** Iterates over contents in reverse LIFO order. */ public Iterator<Item> iterator() { return new ReverseArrayIterator(); } private class ReverseArrayIterator implements Iterator<Item> { private int i; // current position in stack public ReverseArrayIterator() { i = N-1; } public boolean hasNext() { return i >= 0; } public void remove() { } public Item next() { return a[i−−]; } } }

For the record, my implementation is slightly different than what you see in the book, while being functionally equivalent.

Key points are:

Demonstrate on working code (IteratorExploration).

1.5.2 Bag Iterator

Bag iterator works with linked list, rather than array, and the iterator is similarly straightforward.

private class ListIterator implements Iterator<Item> { private Node current; public ListIterator(Node first) { current = first; } public boolean hasNext() { return current != null; } public void remove() { } public Item next() { Item item = current.item; current = current.next; return item; } }

Note how the iterator once again maintains the state of the iteration, this time with a Node current attribute to point to the individual node in the linked list. The ListIterator is an inner class to Bag so it can access the Node class.

Demonstrate by running the modified Bag class which you find in the GitHub repository for today.

1.5.3 MaxPQ

There is no Iterator defined for the MaxPQ as implemented using a Heap structure. In a way, this makes sense, since one would like to get the elements in the same order that they would be retrieved; however to do this properly, you would have to destructively modify the heap – something you didn’t have to do with either the Stack or Bag iterator.

1.5.4 Symbol Table

The Symbol Table description (p. 366) uses a slightly different API for iteration, and this was based on the difference between a straight Symbol Table (which I introduced last week) and an Ordered Symbol table (which I omitted from discussion before the midterm).

Specifically, a client typically needs to get all keys for a Symbol Table. For Homework3, I made this available as part of the SequentialSearchST class. However, since the Symbol table was not ordered, the keys were returned much like the iterator for a Bag.

So now, I ask you... can we use an Iterator for a Binary Search Tree which will return the keys in sorted order? This is the killer application, if you will, for BSTs. So here are the two interfaces for Symbol Tables (p. 366):

Iterable<Key> keys () // all keys Iterable<Key> keys (Key lo, Key hi) // just keys in [lo..hi]

Naturally, the second method only works if the <Key> generic class is itself Comparable, but that is something you know is true for Binary Search Trees.

But now it is not clear how to implement this interface. The following methods do the trick, and they are patterned after the traversal steps we saw earlier. The solution is to use a Queue to accumulate the keys that are visited during the recursive invocations, and then it is a simple matter for a client to process the values from a Queue in order to retrieve the original keys.

public Iterable<Key> keys() { return keys(min(), max()); } public Iterable<Key> keys(Key lo, Key hi) { Queue<Key> queue = new Queue<Key>(); keys(root, queue, lo, hi); return queue; } private void keys(Node node, Queue<Key> queue, Key lo, Key hi) { if (node == null) return; // check if contained within this range int cmplo = lo.compareTo(node.key); int cmphi = hi.compareTo(node.key); // much like a traversal; builds up state in the queue. if (cmplo < 0) keys(node.left, queue, lo, hi); if (cmplo <= 0 && cmphi >= 0) queue.enqueue(x.key); if (cmphi > 0) keys(node.right, queue, lo, hi); }

Note that the keys method might make two recursive calls. But this is not something to worry about. Why? Because for every Iterator that returns N items, there is typically no way to avoid having ~N performance.

Here, assuming that the tree is balanced, we can see that the number of comparisons made during keys is no worse than:

C(N) = 2 + C(N/2) + C(N/2)
C(N) = 2*C(N/2) + 2

This happens in a balanced BST because the number of children "should be" evenly split between left and right children. Let’s telescope this term a few times:

C(N) = 2*[2*(C(N/4) + 2)] + 2
C(N) = 4*C(N/4) + 4 + 2

C(N) = 4*[2*C(N/8) + 2] + 4 + 2
C(N) = 8*C(N/8) + 8 + 4 + 2

Note that the sum of the additive terms are going to be smaller than 2*8 (more specifically, it will be exactly equal to 2*8 - 2)

If we continue this trend – and we assume – that N is a power of 2, then we have:

C(2n) = 2n*C(N/2n) + 2N-2

Eventually, C(1) is equal to zero, so we will compute the number of comparisons for keys to be ~2N in the worst case.

1.6 HW4 Expression Binary Tree

For HW4 you will see how Binary Trees can be used in different domains.

Demonstrate in class with algs.hw4.Evaluate class.

1.7 Version : 2015/11/25

(c) 2015, George Heineman