420 likes | 723 Views
Using a Directed Graph. Drozdek Chapter 8. Objectives. You will be able to Understand and use a C++ ADT for a directed graph. Describe and implement an algorithm for finding the shortest path between two nodes in a directed graph. New Project. New Project.
E N D
Using a Directed Graph Drozdek Chapter 8
Objectives You will be able to • Understand and use a C++ ADT for a directed graph. • Describe and implement an algorithm for finding the shortest path between two nodes in a directed graph.
Implementation of a Directed Graph ADT • Download to project directory: • http://www.cse.usf.edu/~turnerr/Data_Structures/Downloads/2011_04_25_Directed_Graphs/ • Digraph.h • shortest_path.cpp • NetworkFile.txt • Delete .txt from the .h and .cpp files • Add the .h and .cpp files to the project
Implementation of a Directed Graph ADT • Examine Digraph.h • Adjacency list representation. • The Digraph is a vector of Vertex objects • Node ID is the index. • Element 0 is not used. • Each Vertex object has a data member • Template class T • A list of integers (Node IDs) • Nodes adjacent to this node.
NetworkFile.txt • Example: Digraph with string as template parameter. • Class Digraph method read() sets up the digraph with contents of a file.
NetworkFile.txt 1 2 3 4 5 6 7 8 First number in each list is the number of adjacencies. The then IDs of adjacent cities. Note that Denver has an adjacency to itself. (Sight-seeing flight? )
Adjacencies in NetworkFile.txt Bos 5 Chi 4 LA 1 Den 3 NY 6 SF 2 NO 8 Miami 7
Digraph<T>::read() template <typename T> void Digraph<T>::read(ifstream & inStream) { Vertex vi; int n; // number of adjacent vertices int vertex_id; // the number of a vertex // Create a garbage 0-th value so real indices start with 1 digraph.push_back(vi); while (true) { inStream >> vi.data; if (inStream.eof()) break; vi.adjacencyList.clear(); inStream >> n; // Number of adjacent vertices for (int i = 1; i <= n; i++) { inStream >> vertex_id; assert(inStream.good()); vi.adjacencyList.push_back(vertex_id); } digraph.push_back(vi); } }
Digraph<T>::display( ) template <typename T> void Digraph<T>::display(ostream & out) { out << "Adjacency-List Representation: \n"; for (size_t i = 1; i < digraph.size(); i++) { out << i << ": " << digraph[i].data << "--"; for (list<int>::iterator it = digraph[i].adjacencyList.begin(); it != digraph[i].adjacencyList.end(); it++) out << *it << " "; out << endl; } }
Depth First Search • Public method: void depthFirstSearch(int start = 1); ... template <typename T> inline void Digraph<T>::depthFirstSearch(int start) { vector<bool> unvisited(digraph.size(), true); depthFirstSearch(start, unvisited); }
Internal Depth First Search Method // Internal (recursive) depth first search method template <typename T> void Digraph<T>::depthFirstSearch(int start, vector<bool> & unvisited) { visit(digraph[start].data); unvisited[start] = false; // Traverse the adjacency list, performing depth-first // searches from each unvisited vertex in it. list<int>::iterator it; list<int>::iterator begin = digraph[start].adjacencyList.begin(); list<int>::iterator end = digraph[start].adjacencyList.end(); for (it = begin; it != end; it++) { // check if current vertex has been visited if (unvisited[*it]) { // Do a depth first search from this vertex depthFirstSearch(*it, unvisited); } } }
The visit() Method template <typename T> void Digraph<T>::visit(T& node_data) { cout << node_data << endl; } • A Virtual Method • Intended to be overridden in a derived class in order to do something useful at each node.
get_digraph() void get_digraph() { ifstream network_file; while (true) { cout << "Enter name of network file: "; string filename; cin >> filename; network_file.open(filename.c_str()); if (network_file.is_open()) { break; } cout << "Could not open " << filename << endl; cout << "Please try again" << endl; } digraph.read(network_file); network_file.close(); cout << "The Digraph's adjacency list representation:\n"; digraph.display(cout); cout << endl; }
Initial main() int main() { get_digraph(); cout << endl << "Normal termination" << endl; cin.get(); cin.get(); return 0; }
Depth First Search • Let's do a Depth First Search. int main() { get_digraph(); digraph.depthFirstSearch(); cout << endl << "Normal termination" << endl; cin.get(); cin.get(); return 0; }
Depth First Search from LA Bos 5 Chi 4 LA 1 Den 3 NY 6 SF 2 NO 8 Miami 7
Depth First Search from Boston • Do a depth first search from Boston. int main() { get_digraph(); digraph.depthFirstSearch(5);
Depth First Search from Boston Bos 5 Chi 4 LA 1 Den 3 NY 6 SF 2 NO 8 Miami 7
Providing Our Own Visit Method • Add derived class Digraph2.h • http://www.cse.usf.edu/~turnerr/Data_Structures/Downloads/2011_04_25_Directed_Graphs/ • Delete the .txt
Digraph2.h #pragma once #include "digraph.h" template <typename T> class Digraph2 : public Digraph<T> { public: void visit(T& node_data); }; template <typename T> void Digraph2<T>::visit(T& node_data) { show_length(node_data); } Override visit() method in base class
In shortest_path.cpp #include "Digraph2.h" Digraph2<string> digraph; void show_length(string name) { cout << "Length of " << name << " is " << name.length() << endl; }
Own Visit Method End of Section
Paths • Routing problems – find an optimal path in a network • A shortest path in a digraph. • A cheapest path in a weighted digraph. • Example – a directed graph that models an airline network • Vertices represent cities. • Arcs represent flights connecting cities. • Task: Find most direct route between two cities. • (Fewest flights)
Paths in Directed Graph • Most direct route • Shortest path. • Path from start vertex to destination vertex with minimum number of arcs. • Search algorithm for a shortest path: • A minor modification of the breadth-first search algorithm. • Do a breadth first traversal, keeping track of the predecessor of each node reached. • Stop upon reaching the destination node.
Shortest Path Algorithm • Given a directed graph with nodes 1 to n, a starting node ID, start, and a destination node ID,dest: • Let dist[] be an array of ints • dist[v] will hold the distance from start to node v. • Let pred[] be an array of node IDs. • pred[v] will be the predecessor to node v on a shortest path from start to dest. • Initialize dist[start] to 0 and dist[v] to infinity for all other nodes.
Shortest Path Algorithm • Initialize vertex_queue as a queue of ints, initially containing just the starting vertex ID. • While dest has not been visited and vertex queue is not empty: • Remove the front item from the vertex queueas v. • For each node, w, adjacent to v: • If dist[w] is infinity • Set dist[w] to dist[v]+1 • Set pred[w] to v. • Add w to the vertex queue.
Shortest Path Algorithm • At this point, either we have reached dest or we have exhausted the possibilities. • If dist[dest] is infinity report failure. • Else • Initialize a stack with dest. • Initialize v as dest. • Do the following • Set v to pred[v] • Push v onto the stack • until v is start. • The stack now holds a shortest path from start to dest.
Implementation of Shortest_Path In digraph.h template<typename T> vector<int> Digraph<T>::Shortest_Path(int start, int dest) { int n = digraph.size(); vector<int> dist(n, INT_MAX); // Distance from start vector<int> pred(n, 0); // Predecessor on shortest path int v; // The current vertex queue<int> vertex_queue; vertex_queue.push(start); dist[start] = 0;
Implementation of Shortest_Path while (dist[dest] == INT_MAX && !vertex_queue.empty()) { v = vertex_queue.front(); vertex_queue.pop(); list<int>::iterator it; list<int>::iterator begin = digraph[v].adjacencyList.begin(); list<int>::iterator end = digraph[v].adjacencyList.end(); for (it = begin; it != end; ++it) { int w = *it; if (dist[w] == INT_MAX) { dist[w] = dist[v] + 1; pred[w] = v; vertex_queue.push(w); } } }
Implementation of Shortest_Path // Now reconstruct the shortest path if there is one if (dist[dest] == INT_MAX) { cout << "Destination not reachable from start vertex\n"; return path; } stack<int> reverse_path; reverse_path.push(dest); v = dest; do { v = pred[v]; reverse_path.push(v); } while (v != start);
Implementation of Shortest_Path vector<int> path; while (!reverse_path.empty()) { v = reverse_path.top(); path.push_back(v); reverse_path.pop(); } return path; }
main() int main() { get_digraph(); char response; do { int start, destination; cout << "Number of start city? "; cin >> start; cout << "Number of destination? "; cin >> destination; vector<int> path = digraph.Shortest_Path(start, destination); if (path.size() > 0) { display_path(path); } else { cout << digraph.get_data(destination) << " is unreachable from " << digraph.get_data(start) << endl; } cout << endl << "More (Y or N)?"; cin >> response; }while (response == 'y' || response == 'Y');
A Defect • What happens if we ask for the shortest path from a city to itself?
A Defect • What is the program doing? • What should it do? End of Presentation