CS 136 - Lecture 30

  1. Implementations of Graphs (cont'd.)
    1. Adjacency lists

Implementations of Graphs (cont'd.)

Adjacency lists

An adjacency list is composed of a list of nodes or vertices. Hanging off of each node or vertex is a linked list of edges which are adjacent to that node.
        Vertices                  Edges

Labels of the node list may contain different kinds of information than the labels of the edge lists. For instance, the edge nodes will typically contain the value of the edge.

How are vertices held? Could be vector or linked list or ? See later on how to build dictionaries which allow us to look up names fast. Think of as table for now. Each vertex holds collection of edges that are adjacent to it.

Similarly the list of edges could be any kind of collection (class containing add, remove, contains, and elements) including all kinds of lists and binary search tree. We'll use singly-linked list here.

If Directed Graph then not as symmetric (in either representation).

If undirected then put each edge on on 2 lists (of course, same reference in both lists - therefore changes to one impact the other).

Vertices contain adjacencies collection. Most operations passed on to adjacencies.

class GraphListVertex extends Vertex {
    protected Collection adjacencies; // adjacent edges

    public GraphListVertex(Object key)
    // post: constructs new vertex, not incident to any edge
    {
        super(key); // init Vertex fields
        // new adjacency list
        adjacencies = new SinglyLinkedList();
    }

    public void addEdge(Edge e)
    // pre: e is an edge that mentions this vertex
    // post: adds edge to this vertex's adjacency list
    {
        if (!containsEdge(e)) adjacencies.add(e);
    }

    public boolean containsEdge(Edge e)
    // post: returns true if e appears on adjacency list
    {
        return adjacencies.contains(e);
    }

    public Edge removeEdge(Edge e)
    // post: removes and returns adjacent edge "equal" to e
    {
        return (Edge)adjacencies.remove(e);
    }

    public Edge getEdge(Edge e)
    // post: returns the edge that "equals" e, or null
    {
        Iterator edges = adjacencies.elements();
        while (edges.hasMoreElements())
        {
            Edge adjE = (Edge)edges.nextElement();
            if (e.equals(adjE)) return adjE;
        }
        return null;
  }

    public int degree()
    // post: returns the degree of this node
    { 
        return adjacencies.size(); 
    }

    ...
}

Recall that adding edge to singly-linked list always done at beginning of list (constant time, once find vertex).

Edges connected to a given vertex can be held in order by key. Why might this be good or bad?

abstract public class GraphList implements Graph
{
    protected Dictionary dict;  // label to vertex dictionary
    protected boolean directed; // is graph directed?

    protected GraphList(boolean dir)
    // post: constructs an empty graph.
    //     graph is directed iff dir is true.
    {
        dict = new Hashtable();
        directed = dir;
    }
    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.
    {
        if (dict.containsKey(label)) return; // vertex exists
        GraphListVertex v = new GraphListVertex(label);
        dict.put(label,v);
    }

    public Object get(Object label)
    // pre: label labels a valid vertex
    // post: return label of vertex w/ label "equals" 'label'
    {
        Assert.condition(dict.containsKey(label), "Vertex exists");
        return ((GraphListVertex) dict.get(label)).label();
    }

    public boolean contains(Object label)
    // post: returns true iff vertex with "equals" label exits.
    {
        return dict.containsKey(label);   
    }

    public Edge getEdge(Object label1, Object label2)
    // post: returns actual label of edge between vertices.
    {   
        Assert.condition(dict.containsKey(label1), "Vertex exists");
        Edge e = new Edge(get(label1),get(label2),null,directed); 
        return ((GraphListVertex) dict.get(label1)).getEdge(e);
    }

    public boolean containsEdge(Object vLabel1, Object vLabel2)
    // post: returns true iff edge with "equals" label exists
    {
        Assert.condition(dict.containsKey(vLabel1), "Vertex exists");
        Edge e = new Edge(vLabel1, vLabel2, null, directed); 
        return ((GraphListVertex) dict.get(vLabel1)).containsEdge(e);
    }

    public int size()
    // post: returns the number of vertices in graph
    { 
        return dict.size(); 
    }

    public int degree(Object label)
    // pre: label is a label of a vertex
    // post: returns the degree of vertex
    {
        Assert.condition(dict.containsKey(label), "Vertex exists.");
        return ((GraphListVertex) dict.get(label)).degree();
    }

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

    public Iterator elements()
    // post: returns iterator across all vertices of graph
    {
        return dict.keys();
    }

    public Iterator neighbors(Object label)
    // pre: label labels an existing vertex
    // post: returns an iterator traversing neighbor vertices
    {
    // return towns adjacent to vertex labeled lable
        Assert.condition(dict.containsKey(label),  "Vertex exists");
        return ((GraphListVertex) dict.get(label)).adjacentVertices();
    }


    public void clear()
    // post: removes all vertices from graph
    {
      dict.clear();
    }

    ...
}

Adding nodes handled by dictionary (discuss later). Deleting is more complex since must remove vertex for all edge lists (see below).

Operations on edges depend on whether directed or undirected, e.g.,


public class GraphListUndirected extends GraphList
{
    public GraphListUndirected()
    // post: constructs an undirected graph
    {
        super(false);
    }

    public void addEdge(Object vLabel1, Object vLabel2, Object label)
    // pre: vLabel1 & vLabel2 are labels of existing vertices, v1 & v2
    // post: edge (possibly directed) is inserted btn v1 & v2
    //     if edge new, it is labeled with label (can be null)
    {
      GraphListVertex v1 = (GraphListVertex) dict.get(vLabel1);
      GraphListVertex v2 = (GraphListVertex) dict.get(vLabel2);
      Edge e = new Edge(v1.label(), v2.label(), label, false);
      v1.addEdge(e);
      v2.addEdge(e);
    }

    public Object remove(Object label)
    // pre: label is non-null vertex label
    // post: vertex with "equals" label is removed, if found
    {
        GraphListVertex v = (GraphListVertex)dict.get(label);

    // we need to remove each of the reverse edges:
        Iterator vi = neighbors(label);
        while (vi.hasMoreElements())
        {
            // list of adjacent labels
            Object v2 = vi.nextElement();
            // this will remove both edges:
            removeEdge(label,v2);
        }
        dict.remove(label);
        return v.label();
    }

    public Object removeEdge(Object vLabel1, Object vLabel2)  
    // pre: vLabel1 and vLabel2 are labels of existing vertices
    // post: edge is removed, its label is returned
    {
      GraphListVertex v1 = (GraphListVertex) dict.get(vLabel1);
      GraphListVertex v2 = (GraphListVertex) dict.get(vLabel2);
      Edge e = new Edge(v1.label(), v2.label(), null, false);
      v2.removeEdge(e);
      return (v1.removeEdge(e)).label();
    }
    ...
}
Notice how deleting a vertex is expensive since must delete all adjacent edges.

Adding edges is relatively straightforward: just add it in the adjacency lists of vertices if it is not already there.

Deleting an edge requires a search of the appropriate vertex edge list(s).