1.06k likes | 1.25k Views
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)
E N D
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)
Types of graphs • Graph ADT • Adjacency lists • directed • undirected • Adjacency matrices • directed • undirected
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.
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.
Inheritance • In C++, it is common to establish derived classes from base classes. • This is called ‘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 • };
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
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.
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.
Operations • void L.insert(ListElementType elem) • Precondition: None. • Postcondition: L = L with an instance of elem added to the end of L.
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.
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.
Use of first and next • For iteration • for (p=L.first(); p; p=L.next())
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’
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
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 >;
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.
Class List: public section • template < class ListElementType > • class List { • public: • List() : head(0) { } • virtual void insert(const ListElementType & elem); • friend class ListIter < ListElementType >;
Class List: protected section • protected: • struct Node; • typedef Node * Link; • struct Node { • ListElementType elem; • Link next; • }; • Link head; • }; elem next
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.
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++();
Class ListIter: protected section • protected: • const List < ListElementType > & myList; • List < ListElementType >::Link iterPtr; • ListElementType myEndFlag; • }; Works because of ‘friend’ status.
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 • }
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; • }
AL Base Class • Finally, we can construct an adjacency list base class from • the Graph abstract base class • the ListIter class
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;
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 graph constructor
Class NeighborIter() • class NeighborIter : public IntListIter { • public: • NeighborIter(const ALGraph & G, int startVertex) : • IntListIter (G.vertexList[startVertex], G.n) • { assert(startVertex < G.n); } • };
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
Undirected ALGraph header • class UALGraph : public ALGraph { • public: • UALGraph(int size) : ALGraph(size) { } • virtual void addEdge(int fromV, int toV); • };
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++; • }
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); • };
Implementation for DALGraph • void • DALGraph::addEdge(int fromV, int toV) • { • assert(fromV < n && fromV >= 0 • && toV < n && toV >= 0); • vertexList[fromV].insert(toV); • m++; • }
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).
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.
A Topsort algorithm • If a vertex has no ‘in-edges’, then it can be placed in order right away (see 1 below).
Topsort algorithm (con’t) • Now look for another vertex with no in-edges and take it out of circulation.
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
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’.
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.
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
Step 1 Push all vertices without ‘in-edges’ onto the stack. Push 0 on the stack Push 1 on the stack 1 0 0