Graphs
Made up of vertices and arcs Digraph (directed graph) –All arcs have arrows that give direction –You can only traverse the graph in the direction of the arrows
Undirected graphs Undirected graphs have no arrows on the arcs –Can be thought of as digraphs with parallel arcs in opposite directions
Planar graphs Planar graphs can be drawn with no two arcs intersecting
Tree Special type of graph A connected graph with no loops
Problem solving with graphs Many problems can be formulated as graph problems –Find a path from vertex a to vertex b –Find the shortest path from a to b –Find the lowest cost path from from a to b –For example Internet router systems Find a path from a to b that has low cost and can satisfy certain quality of service constraints
Example The farmer and his goose Classic puzzle –Farmer, Fox, Goose, Grain and River with a rowboat –Farmer can’t leave Fox alone with Goose, or Goose alone with Grain –Initially all on north side of river. –Problem Discover how to get to the south side
Fundamental concept –Use vertexes to describe legal states and edges to describe legal transitions between state FarmerFoxGoose Grain Initial state (NNN N) Goal State (SSS S) –2 4 = 16 possible states (not many) –6 illegal states (N S S ?), (S N N ?), etc. –10 legal states give nodes in graphs, legal moves are arcs –Problem: find a loop free path from initial node to goal node
Proper formulation of the problem makes the solution simple Start (N N N N) (S N S N) (S S S N) (N S N N) (S N S S) (N N N S) (N S N S) (S S S S) End (S S N S) (N N S N) (Farmer, Fox, Goose,Grain) Solution: there are two clear loop free paths from start to end
Graph representations We consider four –Matrix representation –Array/adjacency list for digraph –Pure graph –Edge list
Matrix representation of graphs Consider digraphs Approach 1, an adjacency or connectivity matrix A[i,j] A T T 2 T T 3 T 4 T 5 A[i,j] = True if there is an arc from i to j Note: for an undirected graph the matrix is symmetric
Array/Adjacency List for digraph Array of size N for N vertex N linked lists, one for each vertex
Pure graph Connect vertex to its vertices directly Could use an array/vector/linked list for each node –Here we assume an array of four references to other vertices
Edge list Use a table of edges –Pair (a,b) represents edge from a to b 1,2 1,3 2,3 2,5 3,4 4,5
Vertices Typically have name and other attributes as required Edges Often associated traversal information for the edge within the graph –Physical distance –Cost –Expected latency and queueing time for communication network links –Other quality of service attributes
Advantages/disadvantages of the representations Matrix representation –Quick access to any edge (just use A[i,j]) (check for existence) –Wasteful of space for sparse graphs (few edges) –Hard to add vertices, easy to add edges Array/adjacency list for digraph –More space efficient than matrices for sparse matrix –Takes longer to find edge, easy to add an edge –Quick access for vertices, hard to add vertices Pure graph –Takes longer to find vertices/edges –Easy to add vertices (after finding correct location) Edge list –Space efficient –Need a list of vertex to store other information
Multi-graphs, more complex graphs May have more than one edge going in the same direction between a pair of vertices –Different edges could have different costs and other qualities (such as latency, Quality of Service) –Think of it as a multi-lane highway, we can introduce dummy nodes to simplify if necessary a32b
ADT Graph Create Read Write Iterator (graph traversal) Generate a path between two vertices Generate a least cost path between two vertices
Graph traversal Given a digraph and a starting vertex, travel to all vertices without travelling any arc twice. You can visit a vertex more than once Suppose we start at 1, we cant discover 4 We can’t avoid looking at 2 twice
Graph traversal for digraphs We will look at three algorithms –Depth first traversal using recursion –Depth first traversal using a stack –Breadth first traversal using a queue These can be modified to support algorithms for: –Finding a minimum cost path etc.. We will use the following example
Depth first approach Breadth first approach Start * * * Traverse from 1 to 6 * means that you look but find a visited vertex In general each vertex needs an attribute to indicate whether its been visited * * * 5 6 Do all neighbours before neighbours of neighbours
Depth first traversal using recursion Subroutine Traverse(fromNode) Mark fromNode as visited print(‘visited ‘, fromNode) While fromNode has unvisited neighbour C Do Traverse(C) Print(‘Revisited ‘, fromNode) End While End Start * * * Order of calls Visit 1 Visit 2 Visit 5 Visit 3 Rvisit 5 Visit 6 Visit 4 RVisit 6 Visit 7 Rvisit 6 Rvisit 5 Rvisit 2 Rvisit 1 Need to look at a neighbour to see if its been visited
Recursive depth first traversal // precondition all nodes are initially set to be unvisited (ie. false) void depthFirst(Node node){ System.out.printlin(“Visited “, node); node.setVisited(); // sets visited to true for (Enumeration edgeList = node.elements(); // used for list of edges edgeList.hasMoreElements(); Edge edge = (Edge) edgeList.nextElement()){ if (!edge.getNode().isVisited()){ // only visit unvisited neighbours depthFirst(edge.getNode()); System.out.println(“Backtrack to “, node); }
Depth first traversal using a LIFO stack Subroutine Traverse (firstNode) Mark firstNode as visited Push firstNode onto stack While stack is not empty Do// there are paths to explore top = peek at top element If top has an unvisited neighbour C Then Visit C and mark as visited push C onto stack Else Pop top element of stack // backtrack End If End While Example: Push 1 mark as visited Push 2 mark as visited Push 5 mark as visited Push 3 mark as visited Pop 3 backtrack Push 6 mark as visited Push 4 mark as visited Pop 4 backtrack Push 7 mark as visited Pop 7 backtrack Pop 6 backtrack Pop 5 backtrack Pop 2 backtrack Pop 1 backtrack End
Breadth first traversal using FIFO queue Subroutine Traverse(startNode) Mark startNode as visited Add startNode to queue While queue is not empty Do firstElement = front of queue // don’t remove from queue If firstElement has unvisited neighbour C Then Mark C as visited Add C to rear of queue Else Remove first element from front of queue End If End While Example: Add 1 Add 2 Add 3 Add 4 Remove 1 Add 5 Remove 2 Remove 3 Add 6 Remove 4 Add 7 Remove 5 Remove 6 Remove 7
Finding a path from vertex a to vertex b Can modify the a depth first traversal to stop once b is found The path is stored on: The program stack via local variables created for a recursive call for the recursive version The stack for the non-recursive version
Finding the shortest path in a directed graph Dijkstra’s Algorithm for shortest or least costly path between two vertices in a graph Approach –Find the shortest path from one vertex (the source) to all other vertices –Referred to as a greedy algorithm
Dijkstra’s Algorithm 1. Define a set S of vertices and initialize it to contain source 2. Label each vertex not in S with the minimum distance from the source For those connected directly use the edge cost/weight For others set to infinity 3. Choose a vertex not in S with the minimum distance from the source, add it to S // this is the greedy step 4. Check distances to all vertices not in S, if the latest addition to S provides a direct connection to a vertex X not in S, update the minimum distance to X with its total cost/weight w.r.t. source 5. Repeat from step 3 until all vertices are in S or have infinite distance and cannot be added
// assumes a matrix based implementation for a directed graph // Edge is a matrix with edge weights/costs // numVertex is the number of vertices in the graph // MAXVERTEX is the maximum number of vertices in the graph // We have access to a Set class private int MinDistance [] = new int[MAXVERTEX]; // to keep track of shortest distances from the start vertex to others // supporting method private int getWeight(int begin, int end){ return Edge[begin][end] > 0 ? Edge[begin][end] : Integer.MAX_VALUE; } If trueIf false Java based Implementation
public void Dijkstra(int start){ // where all the work is done Set pathFound = new Set(numVertex); // create empty set S int tempMin = start, end, cost, vertex, minDist; try pathFound.Insert(start); // add first vertex catch (SetException e); MinDistance[start] = 0; // update distances of all vertices connected to start for (vertex = 0; vertex < numVertex; ++vertex) if (vertex != start) MinDistance[vertex] = getWeight(start, vertex);
for( vertex = 0; vertex < numVertex; ++vertex){ minDist = Integer.MAX_VALUE; if (vertex != start){ // be greedy, find a vertex not in S with minimum cost for (end = 0; end < numVertex; ++end){ if (!pathFound.Contains(end)){ if (MinDistance[end] < minDist){ tempMin = end; minDist = MinDistance[end]; } if (minDist < Integer.MAX_VALUE){ // got a vertex to add! try pathFound.Insert(tempMin); // put vertex in S catch (SetException e);
// Update costs of everything connected to the new vertex in S for (end = 0; end < numVertex; ++end){ if (!pathFound.Contains(end)){ cost = getWeight(tempMin, end); if (cost < Integer.MAX_VALUE && minDist + cost < MinDistance[end]) MinDistance[end] = mindist + cost; } // When the routine completes, MinDistance[] has the minimum distance from the start vertex to all others that can be reached // The algorithm can be modified so that each vertex remembers its lowest cost parent so that a path back to the source can be enumerated (try it as an exercise)