Graph ADT Inheritance

106
Graph ADT Inheritance

description

Graph ADT Inheritance. Final Exam. Not Thursday, May 20, 2-3:55am Check Spring course schedule Tuesday, May 18, 2-3:55pm, Here in LSci 185 Use the study sheet Assortment of programming questions Like the last midterm, except, 200 points (half on Tables, Trees, Graphs) - PowerPoint PPT Presentation

Transcript of Graph ADT Inheritance

Page 1: Graph ADT Inheritance

Graph ADT Inheritance

Page 2: Graph ADT Inheritance

Final Exam Not Thursday, May 20, 2-3:55am Check Spring course schedule

Tuesday, May 18, 2-3:55pm, Here in LSci 185

Use the study sheet Assortment of programming questions Like the last midterm, except, 200 points (half on Tables, Trees, Graphs) (half on material from first half of course)

Page 3: Graph ADT Inheritance

Types of graphs

Graph ADT Adjacency lists

directed undirected

Adjacency matrices directed undirected

Page 4: Graph ADT Inheritance

Abstract classes An abstract class encapsulates the most

fundamental operations of the class In C++ we call this an Abstract Base Class

We do not instantiate a Graph ADT Instead we preferring to instantiate one of

the four types of graph sub-ADT’s on the last slide.

Page 5: Graph ADT Inheritance

Base and derived classes

The Graph ADT is a general template for all graphs. It is a base class

The four sub-ADT’s we have defined are derived classes

They share all the characteristics of the base class, plus they add their own implementations.

Page 6: Graph ADT Inheritance

Inheritance In C++, it is common to establish

derived classes from base classes.

This is called ‘inheritance’.

Page 7: Graph ADT Inheritance

Inheritance Hierarchy for Graphs

Graph

AdjList Graph

AdjMatrix Graph

Directed AdjList Graph

Undirected AdjList Graph

Directed AdjMatrix

Graph

Undirected AdjMatrix

Graph

Page 8: Graph ADT Inheritance

The Graph ADT abstract base class

class graph { public: graph(int size) : n(size), m(0) { } virtual int vertexSize() { return n; } virtual int edgeSize() { return m; } virtual void addEdge(int fromV, int toV) = 0;

// specify abstract class protected: int n; // number of vertices int m; // number of edges };

Page 9: Graph ADT Inheritance

C++ notes The constructor

graph(int size) : n(size), m(0) { } has no body to be included in another file

(like that of a derived type), but initializes n to the argument size, and assigns m to 0 recall: n is the number of vertices and m

is the number of edges

Page 10: Graph ADT Inheritance

Deriving Adj. List graphs

The adjacency list graph is a sub-ADT of the Graph ADT.

We will establish it, so that a further classes (directed or undirected adjacency list ADT’s) can be derived from it.

Page 11: Graph ADT Inheritance

Adj. List Graph ADT

Characteristics: A List L stores items of some type,

called ListElementType. The items in the List are ordered; the Lists (1,2) and (2,1) are distinct.

Page 12: Graph ADT Inheritance

Operations void L.insert(ListElementType elem) Precondition: None. Postcondition: L = L with an instance of

elem added to the end of L.

Page 13: Graph ADT Inheritance

first()

bool L.first(ListElementType &elem) Precondition: None. Postcondition: If the list is empty, none.

Otherwise, the variable elem contains the first item in L; the “next” item to be returned is the second in L.

Returns: true if and only if there is at least one element in L.

Page 14: Graph ADT Inheritance

next() bool L.next(ListElementType &elem) Precondition: The “first” operation has been

called at least once. Postcondition: Variable elem contains the

next item in L, if there is one, and the next counter advances by one; if there is no next element, none.

Returns: true if and only if there was a next item.

Page 15: Graph ADT Inheritance

Use of first and next For iteration

for (p=L.first(); p; p=L.next())

Page 16: Graph ADT Inheritance

Using our ALGraph ADT

The adjacency list is essentially a list of linked lists.

The standard List ADT provides a way to iterate through a list, it does not provide a way for two iterative process to work simultaineously however.

One way to solve the dilemma is to create and ‘iterator class’

Page 17: Graph ADT Inheritance

Iterator class An iterator class provides a movable

pointer. We can instantiate as many of them as

we need. In our case, one will iterate through

vertices while the other can be used to iterate through edges

Page 18: Graph ADT Inheritance

friend functions Iterators only make sense however if they

have some other object to iterate on. Therefore, we will allow objects of our

iterator class to have access to the internal (private) portions of our List ADT.

We do this by declaring the class to be a ‘friend class’

friend class ListIter < ListElementType >;

Page 19: Graph ADT Inheritance

List and ListIter Our List class will only construct the list

and insert new members

Our List iterator class (ListIter) will move pointers around the List as we need to perform various key List operations.

Page 20: Graph ADT Inheritance

Class List: public section template < class ListElementType > class List { public: List() : head(0) { } virtual void insert(const ListElementType & elem); friend class ListIter < ListElementType >;

Page 21: Graph ADT Inheritance

Class List: protected section

protected: struct Node; typedef Node * Link; struct Node { ListElementType elem; Link next; }; Link head; }; elem next

Page 22: Graph ADT Inheritance

ListIter considerations

We will want to pass a list to the ListIter so that it constructs a pointer to that List and guarantees that the list will be not change through iteration (const)

We will also need to have some indication of when to stop iterating. Perhaps a sentinel value marking the end of the List under consideration.

Page 23: Graph ADT Inheritance

Class ListIter: public section

template < class ListElementType > class ListIter { public: // constructor makes const List called myList // I is the list the iterator will work on ListIter(

const List < ListElementType > & l, ListElementType endFlag )

: myList(l), myEndFlag(endFlag), iterPtr(0) { } virtual ListElementType operator++();

Page 24: Graph ADT Inheritance

Class ListIter: protected section

protected: const List < ListElementType > & myList; List < ListElementType >::Link iterPtr; ListElementType myEndFlag; }; Works because

of ‘friend’ status.

Page 25: Graph ADT Inheritance

Overloaded operator++

template < class ListElementType > ListElementType ListIter < ListElementType > :: operator++() { if (iterPtr == 0) // if NULL point to head iterPtr = myList.head; else // else point to next Link iterPtr = iterPtr->next; // Now that the pointer is advanced... if (iterPtr) // if it now points to a Link return iterPtr->elem; // return the element else // else it points to NULL return myEndFlag; // return end-of-list }

Page 26: Graph ADT Inheritance

insert() template < class ListElementType > void List < ListElementType > ::

insert(const ListElementType & elem) { // prepend to list Link addedNode = new Node; assert(addedNode); addedNode->elem = elem; addedNode->next = head; head = addedNode; }

Page 27: Graph ADT Inheritance

AL Base Class

Finally, we can construct an adjacency list base class from the Graph abstract base class the ListIter class

Page 28: Graph ADT Inheritance

Adjacency List Base Class header

#include "cx12-1.h" // graph base class #include "cx12-2.h" // list class

typedef List < int > IntList; typedef ListIter < int > IntListIter;

Page 29: Graph ADT Inheritance

Class ALGraph()

class ALGraph : public graph { public: ALGraph(int size) : graph(size) { vertexList = new IntList[n]; assert(vertexList); } friend class NeighborIter; protected: IntList * vertexList; };

call the graphconstructor

Page 30: Graph ADT Inheritance

Class NeighborIter()

class NeighborIter : public IntListIter { public: NeighborIter(const ALGraph & G, int startVertex) : IntListIter (G.vertexList[startVertex], G.n) { assert(startVertex < G.n); } };

Page 31: Graph ADT Inheritance

UALists and DALists Now that we have defined

Graph abstract base class Iterator class Adjacency list class

We can derive the bottom-most ADTs Undirected adjacency lists Directed adjacency lists

Page 32: Graph ADT Inheritance

Undirected ALGraph header

class UALGraph : public ALGraph { public: UALGraph(int size) : ALGraph(size) { } virtual void addEdge(int fromV, int toV); };

Page 33: Graph ADT Inheritance

Implementation of UALGraph

void UALGraph::addEdge(int fromV, int toV) { assert(fromV < n && fromV >= 0 && toV < n && toV >= 0); vertexList[fromV].insert(toV); vertexList[toV].insert(fromV); m++; }

Page 34: Graph ADT Inheritance

DALGraph header

#include "cx12-4.h" // ALGraph -- Adjacency List Base Class

class DALGraph : public ALGraph { public: DALGraph(int size) : ALGraph(size) { } virtual void addEdge(int fromV, int toV); };

Page 35: Graph ADT Inheritance

Implementation for DALGraph

void DALGraph::addEdge(int fromV, int toV) { assert(fromV < n && fromV >= 0 && toV < n && toV >= 0); vertexList[fromV].insert(toV); m++; }

Page 36: Graph ADT Inheritance

Topological Sorting A topological sort is a method of arranging the

nodes of a graph in some logical order. Example: the course prerequisites. The problem:

given a partial order of nodes in a graph find the total order that is consistent with the partial

order (in other words, rearrange the vertices without changing the edge relationships so we can see a linear progression).

Page 37: Graph ADT Inheritance

Partial Order Example

0 1

3 4

22

1

3

5

4

Page 38: Graph ADT Inheritance

Topsort solutions 0,1,2,3,4 0,2,3,1,4 There is often more than one total order

that displays the characteristics of the partial order.

Page 39: Graph ADT Inheritance

A Topsort algorithm

If a vertex has no ‘in-edges’, then it can be placed in order right away (see 1 below).

0 1

3 4

22

1

3

5

4

Page 40: Graph ADT Inheritance

Topsort algorithm after one step

0 1

3 4

21

Page 41: Graph ADT Inheritance

Topsort algorithm (con’t)

0 1

3 4

21

Now look for another vertex with no in-edges and take it out of circulation.

Page 42: Graph ADT Inheritance

Topsort after the second step

0 1

3 4

212

Page 43: Graph ADT Inheritance

Topsort (con’t) Continue until all vertices have been considered.

Resulting total order: 1, 0, 3, 2, 4

Note, this retains the original partial order

Page 44: Graph ADT Inheritance

Original partial order

0 1

3 4

22

1

3

5

4

Page 45: Graph ADT Inheritance

Topsort problem

Topsort only works as long as the graph lends itself to ordering.

Graphs with cycles in them cannot be topologically sorted because there is a portion in which every node had an ‘in-edge’.

Page 46: Graph ADT Inheritance

Graph with a cycle

0 1

2

Page 47: Graph ADT Inheritance

Improvements To improve the Topsort algorithm we have a

number of things we can do. First, we must recognise cycles and abort. Second, when we search for nodes with no ‘in-

edges’ we should start with the likeliest candidates. These are nodes that were formerly adjacent to the last one we selected in our sort. After all, they just lost an in-edge and may not have any left.

Page 48: Graph ADT Inheritance

Topsort algorithm nextLabel = 1 find all vertex with no in-edges and push them onto a stack while the stack is not empty do pop a vertex v from the stack label(v) = nextLabel add 1 to nextLabel remove all other edges from v if any neighbor of v now has no in-edge, push it onto the stack if all vertices labeled, report the labels else report that the digraph has a cycle

Page 49: Graph ADT Inheritance

One Step in a Topological Sort

0 1

34

210

2

10

1

Page 50: Graph ADT Inheritance

Step 1

0 0

1

Push 0 on the stack

Push 1 on the stack

Push all vertices without ‘in-edges’ onto the stack.

Page 51: Graph ADT Inheritance

0 1

34

210

2

10

1

Page 52: Graph ADT Inheritance

Step 2

0

1

Pop 1 off the stack

Topsort order

Pop off the top element and label it in topological order.

1

Page 53: Graph ADT Inheritance

0 1

34

210

2

10

1

Page 54: Graph ADT Inheritance

Step 3

0

3

Push 3 on the stack

Push all new vertices without ‘in-edges’ onto to stack.

1

Topsort order

1

Page 55: Graph ADT Inheritance

0 1

34

210

2

10

1

Page 56: Graph ADT Inheritance

Step 4

1

Topsort order

1

0

Pop 3 off the stack

32

Page 57: Graph ADT Inheritance

0 1

34

210

2

10

1

Page 58: Graph ADT Inheritance

Step 5

1

Topsort order

1

0

Pop 0 off the stack

32

3

Page 59: Graph ADT Inheritance

Step 6

1

Topsort order

1

0

32

3

2

Push 2 on the stack

Page 60: Graph ADT Inheritance

Step 7

Pop 2 off the stack

1

Topsort order

1

0

32

3

24

Page 61: Graph ADT Inheritance

Step 8

4

Push 4 on the stack

1

Topsort order

1

0

32

3

24

Page 62: Graph ADT Inheritance

Step 9

Pop 4 off the stack

1

Topsort order

1

0

32

3

24

45

Page 63: Graph ADT Inheritance

Topological Sort #include "cx12-7.h" // directed adjacency list graphs #include "sx8-1.h" // stacks (supplemental version) #include "cx9-4.h" // queues #include <fstream.h>

int main() { // read graph from a file // first entry is size of graph const char * inFileName = "graph.dat"; ifstream ifs(inFileName); assert(ifs); // make sure graph exists

Page 64: Graph ADT Inheritance

Topological sort (con’t)

int n; ifs >> n; DALGraph G(n); cout << "Created graph; n = " << G.vertexSize() << endl; // now read in the edges and add to the graph int u, v; while ( ifs >> u ) { ifs >> v; G.addEdge(u,v); } cout << "Edges in graph: m = " << G.edgeSize() << endl;

Page 65: Graph ADT Inheritance

Topological sort (con’t)

// count the number of in-edges for each vertex int * vertices(new int[n]); assert(vertices); for (u = 0; u < n; u++) vertices[u] = 0; for (u = 0; u < n; u++) { NeighborIter ni(G,u); while ((v = ++ni) != n) vertices[v]++; }

Page 66: Graph ADT Inheritance

Topological sort (con’t)

// put vertices with no in-edge onto a stack Stack < int > s; for (u = 0; u < n; u++) if (vertices[u] == 0) s.push(u); if (s.isEmpty()) { cout << "graph has a cycle!\n"; return 0; }

Page 67: Graph ADT Inheritance

Topological sort (con’t) // begin topological sort // As each vertex is identified, put it into a queue and // decrement the number of in-edges for its neighbors int count = 0; // number of vertices found so far Queue < int > sortedEdges; while (!s.isEmpty()) { count++; u = s.pop(); sortedEdges.enqueue(u); // reduce in count for u's neighbors; // for each that goes to zero, put on stack NeighborIter ni(G,u);

Page 68: Graph ADT Inheritance

Topological sort (con’t)

while ((v = ++ni) != n) { --vertices[v]; if (vertices[v] == 0) s.push(v); } }

Page 69: Graph ADT Inheritance

Topological sort (con’t)

// check results if (count < n) cout << "Couldn't complete top sort -- cycle

present.\n"; cout << "Ordering for top sort: \n"; while (!sortedEdges.isEmpty()) cout << sortedEdges.dequeue() << '\t'; cout << endl; return n; }

Page 70: Graph ADT Inheritance

The Adjacency Matrix Classes

We have just examined topological sorting with an adjacency list class

Now we turn to the adjacency matrix class to show how it can be used to demonstrate transitive closure.

Page 71: Graph ADT Inheritance

Ad. Matrix Base Class #include "cx12-1.h" // graph base class

class amGraph : public graph { public: amGraph(int size); virtual bool edgeMember(int fromV, int toV); protected: int * * am; // am points to a pointer to int };

Page 72: Graph ADT Inheritance

Implementation amGraph::amGraph(int size) : graph(size) { int i; am = new int * [n]; // make an array of pointers to int assert(am); for (i = 0; i < n; i++) { am[i] = new int[n]; // make an array of ints assert(am[i]); int j; for (j = 0; j < n; j++) // initialize the array to 0 am[i][j] = 0; } }

Page 73: Graph ADT Inheritance

edgeMember()

bool amGraph::edgeMember(int fromV, int toV) { assert (fromV < n && toV < n && fromV >= 0 && toV >= 0); return bool(am[fromV][toV] != 0); }

Page 74: Graph ADT Inheritance

Derived types of Adjacency Matrix

Now that we have defined the adjacency matrix, we are ready to proceed to our definitions of its two sub-ADTs

Directed Adjacency Matrix (DAM) Undirected Adjacency Matrix (UAM)

Page 75: Graph ADT Inheritance

DAM Class header

#include "cx12-10.h" // adjacency matrix base class

class dAMGraph : public amGraph { public: dAMGraph(int size, int initialValue = 0) : amGraph(size) { } virtual void addEdge(int fromV, int toV); };

Page 76: Graph ADT Inheritance

Implementation of DAM Class

void dAMGraph::addEdge(int fromV, int toV) { assert(fromV < n && toV < n && fromV >= 0 && toV >= 0); if (!edgeMember(fromV, toV)) { m++; am[fromV][toV] = 1; } }

Page 77: Graph ADT Inheritance

UAM header file

class uAMGraph : public amGraph { public: uAMGraph(int size, int initialValue = 0) : amGraph(size) { } virtual void addEdge (int fromV, int toV); };

Page 78: Graph ADT Inheritance

Implementation of UA Matrix Class

void uAMGraph::addEdge(int fromV, int toV) { assert(fromV < n && toV < n && fromV >= 0 && toV >= 0); if (!edgeMember(fromV, toV)) { m++; am[fromV][toV] = 1; am[toV][fromV] = 1; } }

Page 79: Graph ADT Inheritance

Using UAM class The problem: Nodes on a network can

communicate between each other if there is a path.

Construct a ‘transitive closure’ graph that indicates all the possible communications links between nodes along paths

Page 80: Graph ADT Inheritance

A Communications Network

1 3

2 4 6

5 7

8

Page 81: Graph ADT Inheritance

dAM graph implementation

1 2 3 4 5 6 7 81 1 1 1 1 0 0 0 02 1 1 0 0 0 0 0 03 1 0 1 0 0 1 0 04 1 0 0 1 0 0 0 05 0 0 0 0 1 0 1 06 0 0 1 0 0 1 0 07 0 0 0 0 1 0 1 18 0 0 0 0 0 0 1 1

Page 82: Graph ADT Inheritance

Definition: path

A sequence [v0,v1,…,vk] is a path from v0 to vk in an undirected graph G = (V,E) if and only if {vi, vj+1} E for all 1<= i < k. The length of the path is k.

OR... A sequence of vertices is a path if each pair of

vertices constitutes an edge.

Page 83: Graph ADT Inheritance

Definition: transitive closure

For a graph G = (V, E), the transitive closure graph G* = (V, E*) has edge {v1,v2} E* if and only if there's a path [v1,…,v2] in graph G.

Page 84: Graph ADT Inheritance

Transitive Closure of Graph

1 3

2 4 6

5 7

8

Page 85: Graph ADT Inheritance

Transitive closure graph

1 2 3 4 5 6 7 81 1 1 1 1 0 1 0 02 1 1 1 1 0 1 0 03 1 1 1 1 0 1 0 04 1 1 1 1 0 1 0 05 0 0 0 0 1 0 1 16 1 1 1 1 0 1 0 07 0 0 0 0 1 0 1 18 0 0 0 0 1 0 1 1

Page 86: Graph ADT Inheritance

Transitive closure algorithm

Mark all length 1 paths Revisit all vertices at the end of length 1

paths. If they have links other than the original, mark the length 2 paths for the original

Continue until all possible path lengths have been examined. (many nested for loops)

Page 87: Graph ADT Inheritance

Transitive closure int main() { const char * inFileName = "graph2.dat"; // read graph from a file // first entry is size of graph ifstream ifs(inFileName); assert(ifs); int n; ifs >> n; uAMGraph G(n); cout << "Created graph; n = " << G.vertexSize() << endl; int u, v;

Page 88: Graph ADT Inheritance

continued while ( ifs >> u ) { ifs >> v; G.addEdge(u,v); } cout << "Edges in graph: m = " << G.edgeSize() << endl; int step; for (step=0; step < n; step++) for (u = 0; u < n; u++) // rows for (v = 0; v < n; v++) // columns if (G.edgeMember(u,step) && G.edgeMember(step,v)) G.addEdge(u,v);

Page 89: Graph ADT Inheritance

continued // print results for (u=0; u < n; u++) { cout << u << "\t: "; for (v = 0; v < n; v++) cout << (G.edgeMember(u,v)? "T " : "F "); cout << endl; } return 0; }

Page 90: Graph ADT Inheritance

Chapter Summary

A graph represents relationships among items. Vertices represent the items, and edges represent the relationships.

In an undirected graph, an edge is a set; in a directed graph, it’s a directed pair.

Page 91: Graph ADT Inheritance

Summary continued

An adjacency list is a data structure for representing a graph, by keeping a list of the neighbor vertices of each vertex.

An adjacency matrix is a data structure for representing a graph, by keeping a matrix of 0’s and 1’s in which each 1 corresponds with an edge.

Page 92: Graph ADT Inheritance

Summary (continued)

An inheritance hierarchy can be used to represent the variations on a graph.

Abstract base classes allow the programmer to specify an interface for inherited objects.

Page 93: Graph ADT Inheritance

Summary (continued) Iterator classes, implemented via friend

classes, provide the most flexible way to create iterators.

A topological sort finds an ordering of vertices consistent with the partial order represented by the edges.

Transitive closure of a graph contains an edge for every path in the original graph.

Page 94: Graph ADT Inheritance

Searching Graphs Must be able to traverse the entire

structure without getting caught up endlessly in a cycle.

Two strategies depth-first breadth-first

Page 95: Graph ADT Inheritance

Sample graph

B

E

H

C

F

I

G

J

D

Page 96: Graph ADT Inheritance

Depth first search Choose a node to start at and mark it visited Find a connected node which has nost been

marked and mark it visited Repeat step 2 until the end of the path has

been found Return to the last node with unvisited

neighbors and repeat steps 2-4 until there are no unvisited nodes

Page 97: Graph ADT Inheritance

DFS The Depth-First Search (DFS) algorithm can be

used to traverse all the vertices of a graph. DFS works as follows:

put the initial vertex into a stack; while the stack is not empty do{ pop a vertex v from the stack; visit vertex v; push all unvisited neighbors of v onto the

stack; }

Page 98: Graph ADT Inheritance

Example

B

E

H

C

F

I

G

J

D

Visit BVisit EVisit HReturn to EVisit IReturn to EReturn to BVisit FVisit JReturn to FVisit CVisit GReturn to CVisit DReturn to C,F,B

B E H I F J C G D

Page 99: Graph ADT Inheritance

Depth first considerations

There are many possible depth-first traversal orders depending on What node you start at What order you visit nodes in

However, from a given node, there is only one unique traversal for a given ordering principle (ie. left to right)

Page 100: Graph ADT Inheritance

Implementing DFSearch

Use a UAM ADT G.connected(node1, node2) returns T/F

Use UAM or UAL Graph ADT to implement G.visited(node) returns visited T/F

Use boolean array indexed by node Use a queue to store visited node names Use a stack to store previously visited nodes

Page 101: Graph ADT Inheritance

Stacks and queues

B E H I F J C G D

front rear

BBEBEHBEBEIBEBBFBFJBFBFCBFCGBFCBFCDBFCBFB

B

E

H

C

F

I

G

J

D

Page 102: Graph ADT Inheritance

Breadth first Search Like DFS, except we visit all edges of a

node first instead of travelling down a path

Page 103: Graph ADT Inheritance

BFS algorithm Choose a node to start at and mark it visited Find a connected node which has not been

marked visited and mark it visited Find another connected node and mark it

visited Repeat until no more unvisited connected

nodes Do for each node

Page 104: Graph ADT Inheritance

Sample graph

B

E

H

C

F

I

G

J

D

Page 105: Graph ADT Inheritance

Example

B

E

H

C

F

I

G

J

D Visit BVisit EVisit FVisit CReturn to EVisit HVisit IReturn to FVisit JReturn to CVisit GVisit D

B E F C H I J G D

Page 106: Graph ADT Inheritance

Two queues, no stack

B E F C H I J G D

front rear

BBEBEFBEFCEFCEFCHEFCHIFCHIFCHIJCHIJCHIJGCHIJGDHIJGDIJGDJGDGDD

B

E

H

C

F

I

G

J

D