CS136, Lecture 27

    1. Iterators for binary tree
      1. Preorder traversal
      2. Inorder traversal
      3. Post-order traversal
      4. Level-order traversal
  1. Graphs
    1. A.Definition and Terminology
    2. B. Sample Graph Problems
    3. C. The Graph Interface
    4. D. Implementations of Graphs

Iterators for binary tree.

Skipped earlier

Preorder traversal

Look at preorder traversal. Recall recursive version:

public void doPreorder()
{
    reset();    // move cursor to root
    if (root != null) doRecPreorder();
}

/*
    post:  Do preorder traversal of subtree pointed to by 
            cursor.  Return cursor to starting point when done.
*/
public void doRecPreorder()
{
    value().doValueOp();
    if (hasLeft())
    {
        moveLeft();
        doRecInorder();
        moveUp();
    }
    if (hasRight())
    {
        moveRight();
        doRecPreorder();
        moveUp();
    }
}
Only problem is can't take one step, and then stop and save where are so can come back later to do it. No problem with iterative since save stack.

Idea is top element on stack is element currently examining. Must traverse subtree headed by node on stack as well as all subtrees headed by all other nodes on stack.

All but nextElement() are pretty trivial:

class BinaryTreePreorderIterator implements Iterator {
    protected BinaryTreeNode root;  // root of tree
    protected Stack todo;              // helper stack

    public BinaryTreePreorderIterator(BinaryTreeNode root)
    // post: constructs an iterator to traverse in preorder
    {
        todo = new StackList();
        this.root = root;
        reset();
    }   

    public void reset()
    // post: resets the iterator to retraverse
    {
        todo.clear();
        // stack is empty.  Push on the current node.
        if (root != null) todo.push(root);
    }

    public boolean hasMoreElements()
    // post: returns true iff iterator is not finished
    {
        return !todo.isEmpty();
    }

    public Object value()
    // pre: hasMoreElements()
    // post: returns reference to current value
    {   
        return ((BinaryTreeNode)todo.peek()).value();
    }

    public Object nextElement()
    // pre: hasMoreElements();
    // post: returns current value, increments iterator
    {
        BinaryTreeNode old = (BinaryTreeNode)todo.pop();
        Object result = old.value();

        if (old.right() != null) todo.push(old.right());
        if (old.left() != null) todo.push(old.left());
        return result;
    }
}

Inorder traversal

The code for an in-order traversal is not much harder. Idea now is if node is on stack, have already traversed left subtree, but still need to do node and right subtree. Note first node to traverse is obtained by going all way down left branch. After do root of tree, go to right child, and then follow leftmost branch all way down.

class BinaryTreeInorderIterator implements Iterator {
    protected BinaryTreeNode root;  // root of tree
    protected Stack todo;           // helper stack

    public BinaryTreeInorderIterator(BinaryTreeNode root)
    // post: constructs an iterator to traverse inorder
    {
            todo = new StackList();
            this.root = root;
            reset();
    }   

    public void reset()
    // post: resets the iterator to retraverse
    {
            todo.clear();
            // stack is empty.  Push on nodes from root to
            // leftmost descendent
            BinaryTreeNode current = root;
            while (current != null) {
                todo.push(current);
                current = current.left();
            }
    }

    public boolean hasMoreElements()
    // post: returns true iff iterator is not finished
    {
            return !todo.isEmpty();
    }

    public Object value()
    // pre: hasMoreElements()
    // post: returns reference to current value
    {   
            return ((BinaryTreeNode)todo.peek()).value();
    }

    public Object nextElement()
    // pre: hasMoreElements();
    // post: returns current value, increments iterator
    {
            BinaryTreeNode old = (BinaryTreeNode)todo.pop();
            Object result = old.value();
            // this node has no unconsidered left children.
            // if this node has a right child, 
            //  push the right child and its leftmost descendants:
            // now top elt of stack is next node to be visited.
            if (old.right() != null) {
                BinaryTreeNode current = old.right();
                do {
                    todo.push(current);
                    current = current.left();
                } while (current != null);
            }
            return result;
    }
}


Post-order traversal

Postorder is hardest - skip that here

Level-order traversal

Level order easy with queue. When process element, put its children on queue!
    public Object nextElement()
    // pre: hasMoreElements();
    // post: returns current value, increments iterator
    {
        BinaryTreeNode current = (BinaryTreeNode)todo.dequeue();
        Object result = current.value();
        if (current.left() != null) todo.enqueue(current.left());
        if (current.right() != null) todo.enqueue(current.right());
        return result;
    }
Note similarity to preorder traversal!

Graphs

A. Definition and Terminology

A graph is a collection of nodes or vertices, joined by edges. Vertices have labels. Edges can also have labels (often weights).

Directed graphs differ from undirected graphs in that each edge is given a direction.

Two vertices are adjacent if there exists an edge between them

A path is a sequence of adjacent vertices.

Simple path if no vertices repeated (except first and last may be same).

Simple path is cycle if first and last are same

B. Sample Graph Problems

Many problems can be converted to graph problems:

7 bridges of Königsberg: We will present Euler's solution to this famous problem.

The problem: Can you walk over all of the 7 bridges and return to where you started while crossing every bridge exactly once?

We can make this into a graph problem by adding sidewalks and information booths:

If we remove the rest of the picture and only keep the sidewalks we end up with a graph:

We can now restate the problem as follows: Can we make a complete circuit of the graph, traversing every edge exactly once and returning where started?

Many important graph algorithms examined in Algorithms class!

C. The Graph Interface

public interface Graph extends Collection
{
    public void add(Object label);
    // pre: label is a non-null label for vertex
    // post: a vertex with label is added to graph.
    //   if vertex with label is already in graph, no action.

    public void addEdge(Object vtx1, Object vtx2, Object label);
    // pre: vtx1 and vtx2 are labels of existing vertices
    // post: an edge (possibly directed) is inserted between
    //       vtx1 and vtx2.

    public Object remove(Object label);
    // pre: label is non-null vertex label
    // post: vertex with "equals" label is removed, if found

    public Object removeEdge(Object vLabel1, Object vLabel2);  
    // pre: vLabel1 & vLabel2 are labels of existing vertices
    // post: edge is removed, its label is returned

    public Object get(Object label);
    // post: returns actual label of indicated vertex

    public Edge getEdge(Object label1, Object label2);
    // post: returns actual label of edge between vertices.

    public boolean contains(Object label);
    // post: return true iff vertex w/ "equals" label exists.

    public boolean containsEdge(Object vLabel1,Object vLabel2);
    // post: returns true iff edge with "equals" label exists

    public boolean visit(Object label);
    // post: sets visited flag on vertex, returns prev. value

    public boolean visitEdge(Edge e);
    // pre: sets visited flag on edge; returns previous value

    public boolean isVisited(Object label);
    // post: returns visited flag on labelled vertex

    public boolean isVisitedEdge(Edge e);
    // post: returns visited flag on edge between vertices

    public void reset();
    // post: resets visited flags to false

    public int size();
    // post: returns the number of vertices in graph

    public int degree(Object label);
    // pre: label labels an existing vertex
    // post: returns the no. of vertices adjacent to vertex

    public int edgeCount();
    // post: returns the number of edges in graph

    public Iterator elements();
    // post: returns iterator across all vertices of graph

    public Iterator neighbors(Object label);
    // pre: label is label of vertex in graph
    // post: returns iterator over vertices adj. to vertex
    //      each edge beginning at label visited exactly once

    public Iterator edges();
    // post: returns iterator across edges of graph
    //       iterator returns edges; each edge visited once

    public void clear();
    // post: removes all vertices from graph

    public boolean isEmpty();
    // post: returns true if graph contains no vertices

    public boolean isDirected();
    // post: returns true if edges of graph are directed
}

D. Implementations of Graphs

Classes to represent vertices and edges. Quite simple:

class Vertex
{
    protected Object label; // the user's label
    protected boolean visited;  // this vertex visited

    public Vertex(Object label)
    // post: constructs unvisited vertex with label
    {
            Assert.pre(label != null, "Vertex key is non-null");
            this.label = label;
            visited = false;
    }

    public Object label()
    // post: returns user label associated w/vertex
    {
            return label;
    }

    public boolean visit()
    // post: marks vertex as being visited.
    {
            boolean was = visited;
            visited = true;
            return was;
    }

    public boolean isVisited()
    // post: returns true iff vertex has been visited
    {
            return visited;
    }
 
    public void reset()
    // post: marks vertex unvisited
    {
            visited = false;
    }
    
    public boolean equals(Object o)
    // post: returns true iff vertex labels are equal
    {
            Vertex v = (Vertex)o;
            return label.equals(v.label());
    }

    public int hashCode()
    // post: returns hash code for vertex
    {
            return label.hashCode();
    }

    public String toString()
    // post: returns string representation of vertex.
    {
            return "[Vertex: "+label+"]";
    }

========================================================
public class Edge
{
    protected Object[] vLabel;  // labels of adjacent vertices
    protected Object label;     // edge label
    protected boolean visited;  // this edge visited
    protected boolean directed; // this edge directed

    public Edge(Object vtx1, Object vtx2, Object label, boolean directed)
    // post: edge associates vtx1 & vtx2. labeled with label.
    //       directed if "directed" set true
    {
            vLabel = new Object[2];
            vLabel[0] = vtx1;
            vLabel[1] = vtx2;
            this.label = label;
            visited = false;
            this.directed = directed;
    }

    public Object here()
    // post: returns first node in edge
    {
            return vLabel[0];
    }

    public Object there()
    // post: returns second node in edge
    {
            return vLabel[1];
    }

    public void setLabel(Object label)
    // post: sets label of this edge to label 
    {
            this.label = label;
    }

    public Object label()
    // post: returns label associated with this edge
    {
            return label;
    }

    public boolean visit()
    // post: visits node, returns whether previously visited
    {
            boolean was = visited;
            visited = true;
            return was;
    }

    public boolean isVisited()
    // post: returns true iff node has been visited
    {
            return visited;
    }

    public void reset()
    // post: resets edge's visited flag to initial state
    {
            visited = false;
    }

    public int hashCode()
    // post: returns suitable hashcode.
    {
            if (directed) 
                    return here().hashCode()-there().hashCode();
            else          
                    return here().hashCode()^there().hashCode();
    }   // ^ gives bitwise exclusive or

    public boolean equals(Object o)
    // post: returns true iff edges connect same vertices
    {   
            Edge e = (Edge)o;
            return ((here().equals(e.here()) && 
                                            there().equals(e.there())) ||
                (!directed && (here().equals(e.there()) && 
                                        there().equals(e.here()))));
    }
    
    public String toString()
    // post: returns string representation of edge
    {
            StringBuffer s = new StringBuffer();
            s.append("");
            else s.append("->");
            s.append(" "+there());
            return s.toString();
    }
}
If there are a fixed number of edges from each node then we can have fixed number of edges stored with each node (like a binary tree).

Otherwise we typically use an adjacency matrix or adjacency lists.

Example: Here is an undirected graph of the Northeastern states.

NY, Vt, NH, ME, MA, CN, RI

We will draw a line between capitals if the corresponding states share a common border:

We will represent this graph as both an adjacency matrix and an adjacency list.