360 likes | 575 Views
Dijkstra's Algorithm. Using a Weighted Graph. Objective. You will be able to: Describe an ADT for a weighted graph. Implement an ADT for a weighted graph. Implement Dijkstra's algorithm for finding the minimum cost path between two nodes in a weighted graph. Edsger Dijkstra.
E N D
Dijkstra's Algorithm Using a Weighted Graph
Objective • You will be able to: • Describe an ADT for a weighted graph. • Implement an ADT for a weighted graph. • Implement Dijkstra's algorithm for finding the minimum cost path between two nodes in a weighted graph.
Edsger Dijkstra • 1959 Shortest Path Algorithm (Graph Theory) • 1968 The "THE" Operating System • 1968 "Go To Consider Harmful" (Niklaus Wirth, Editor) • 1972 ACM Turing Award • 1984 - 2002 University of Texas • http://en.wikipedia.org/wiki/Edsger_W._Dijkstra 1930 - 2002
A Weighted Graph ADT • A Set of Nodes (Vertices) • Numbered 1 .. N • A Set of bidirectional Edges (pairs of Nodes) • A nonnegative weight for each edge. • "Cost" or "Distance"
Problem: Find Best Path • Find a Path from a specified starting node to a specified destination node such that the sum of the weights of the edges is minimized.
A Weighted Graph ADT • Create a new project. • Weighted_Graph_Demo • Add main.cpp • Start with "Hello, World!"
main.cpp #include <iostream> using namespace std; int main (void) { cout << "This is the Weighted Graph Demo\n"; cin.get(); return 0; }
Weighted_Graph.h • Add new item: • Weighted_Graph.h • This will be our weighted graph ADT template. • Will represent the graph with an adjacency matrix. • distance[i][j] is distance from node i to node j • weight of the edge between node i and node j • 0 if no edge between i and j
The Weighted Graph ADT • Nodes will be an arbitrary type. • Template parameter T • Must define operators =, ==, and << • Externally nodes are identified by objects of class T. • Example: Strings • Internally nodes are identified by integer Node IDs, 1 ... N
Weighted_Graph.h #pragma once #ifndef MAX_NODES #define MAX_NODES 100 #endif using namespace std; template <typename T> class Weighted_Graph { private: int number_of_nodes; // The Node ID 0 is not used. The first real node has ID 1 T nodes[MAX_NODES+1]; int distance[MAX_NODES+1][MAX_NODES+1]; public: Weighted_Graph(); void Add_Node(const T& node); void Add_Edge(const T& Node_1, const T& Node_2, int dist); void Display() const; };
Implementation • Constructor template <typename T> Weighted_Graph<T>::Weighted_Graph() : number_of_nodes(0) {};
Add_Node template <typename T> void Weighted_Graph<T>::Add_Node (const T& node) { assert(number_of_nodes < MAX_NODES); int n = ++number_of_nodes; nodes[n] = node; for (int i = 1; i < n; ++i) { distance[i][n] = 0; distance[n][i] = 0; } }
Add_Edge template <typename T> void Weighted_Graph<T>::Add_Edge(const T& Node_1, const T& Node_2, int dist) { int Node_ID_1 = Get_Node_ID(Node_1); int Node_ID_2 = Get_Node_ID(Node_2); assert ((Node_ID_1 > 0) && (Node_ID_2 > 0)); distance[Node_ID_1][Node_ID_2] = dist; distance[Node_ID_2][Node_ID_1] = dist; } Add at top: #include <cassert>
Get_Node_ID template <typename T> int Weighted_Graph<T>::Get_Node_ID(const T& node) const { for (int i = 1; i <= number_of_nodes; ++i) { if (nodes[i] == node) { return i; } } cout << "Node " << node << " not found\n"; return -1; }
Get_Node_ID Also add to class definition. private: int Get_Node_ID(const T& Node) const;
Display template <typename T> void Weighted_Graph<T>::Display() const { cout << endl; cout << setw(10) << " "; for (int i = 1; i <= number_of_nodes; ++i) { cout << setw(10) << nodes[i] << " "; } cout << endl; for (int i = 1; i <= number_of_nodes; ++i) { cout << setw(10) << nodes[i]; for (int j = 1; j <= number_of_nodes; ++j) { cout << setw(10) << distance[i][j] << " "; } cout << endl; } cout << endl; }
Weighted_Graph.h • Add at top: #include <iostream> #include <iomanip>
Washington Chicago Austin Denver Atlanta Dallas Houston Example: Airline connections 1000 900 600 1400 780 1300 200 800 160
main.cpp #include <iostream> #include <string> #include "Weighted_Graph.h" using namespace std; Weighted_Graph<string> connections;
Add to main() connections.Add_Node(string("Atlanta")); connections.Add_Node(string("Austin")); connections.Add_Node(string("Chicago")); connections.Add_Node(string("Dallas")); connections.Add_Node(string("Denver")); connections.Add_Node(string("Houston")); connections.Add_Node(string("Washington")); connections.Add_Edge(string("Atlanta"), string("Denver"), 1400); connections.Add_Edge(string("Atlanta"), string("Houston"), 800); connections.Add_Edge(string("Atlanta"), string("Washington"), 600); connections.Add_Edge(string("Austin"), string("Dallas"), 200); connections.Add_Edge(string("Austin"), string("Houston"), 160); connections.Add_Edge(string("Chicago"), string("Dallas"), 900); connections.Add_Edge(string("Chicago"), string("Denver"), 1000); connections.Add_Edge(string("Dallas"), string("Denver"), 780); connections.Add_Edge(string("Dallas"), string("Washington"), 1300); connections.Display();
Dijkstra's Algorithm • Given a starting node and a destination node • Push outward from the starting node • Keeping track of the shortest total distance seen so far to each node and the predecessor to that node on the shortest path seen so far. • Initially if the node is adjacent to the start • Shortest total distance seen so far is the weight of the link • Predecessor on best path is the start • If node is not adjacent • Shortest distance seen so far is infinity (INT_MAX)
Dijkstra's Algorithm • On each step of the iteration, the shortest total distance to one more node will be determined • And its predecessor on the best path to it from the starting node. • Keep track of which nodes we know the shortest distance to. • Boolean array indexed by Node ID
Dijkstra's Algorithm • At each step of the iteration: • Determine a node with smallest "Best total distance seen so far" among the nodes for which the best total distance has not yet been determined. • Call it node N. • The best distance so far for node N is the actual best distance. • The predecessor for which that distance was determined is is predecessor on the best path. • The best path to that node is now known.
Dijkstra's Algorithm • Update best distances • For each node, i, for which the best path has not yet been determined -- • Check if the node N provides a better path than the best seen so far. • Is Total Distance[N] + Distance(N,i) less than Best Total Distance Seen So for for Node i? • If so, make that the new best total distance so far and make node N the predecessor.
Dijkstra's Algorithm • Continue the iteration until the actual shortest total distance for the destination has been determined. • We then know the shortest path length from Start to Destination and the best path. • Follow predecessors from Destination back to Start.
Best_Path • Add to Weighted_Graph class defintion: #include <deque> public: ... deque<T> Best_Path(const T& Start, const T& Dest) const; • Will return the best path from Start to Dest to the caller in the form of an STL deque. • doubly ended queue
Best_Path // Dijkstra's Algorithm template <typename T> deque<T> Weighted_Graph<T>::Best_Path(const T& Start, const T& Dest) const { deque<T> best_path; int best_total_distance_so_far[MAX_NODES+1]; int predecessor[MAX_NODES+1]; bool best_total_distance_is_known[MAX_NODES+1]; if (Dest == Start) { cout << "Dest = Start in call to Best_Path\n"; return best_path; // Return empty deque. } int Start_ID = Get_Node_ID(Start); int Dest_ID = Get_Node_ID(Dest); if ((Start_ID <= 0) || (Dest_ID <= 0)) { cout << "Invalid start or destination\n"; return best_path; // empty deque }
Best_Path for (int i = 1; i <= MAX_NODES; ++i) { if (distance[Start_ID][i] > 0) { best_total_distance_so_far[i] = distance[Start_ID][i]; predecessor[i] = Start_ID; } else { best_total_distance_so_far[i] = INT_MAX; predecessor[i] = -1; } best_total_distance_is_known[i] = false; } best_total_distance_so_far[Start_ID] = 0; best_total_distance_is_known[Start_ID] = true;
Best_Path (continued) while (!best_total_distance_is_known[Dest_ID]) { // Determine the node with least distance among // all nodes whose best distance is not yet known. int min_best_dist = INT_MAX; int best_node_id = -1; for (int i = 1; i <= number_of_nodes; ++i) { if (best_total_distance_is_known[i]) { continue; } if (best_total_distance_so_far[i] < min_best_dist) { min_best_dist = best_total_distance_so_far[i]; best_node_id = i; } }
Best_Path (continued) if (best_node_id == -1) { // Destination is unreachable. cout << Dest << " is unreachable from " << Start << endl; return best_path; // empty deque } // Best total distance so farfor this node is the actual // best total distance . int n = best_node_id; best_total_distance_is_known[n] = true;
Best_Path (continued) // Check if this node provdes a better route to the destination // for other nodes whose best distance is not yet known. for (int i = 1; i <= number_of_nodes; ++i) { if (best_total_distance_is_known[i]) { continue; } if (distance[n][i] <= 0) { continue; // No connection from node n to node i } if ((best_total_distance_so_far[n] + distance[n][i]) < best_total_distance_so_far[i]) { // It does. best_total_distance_so_far[i] = best_total_distance_so_far[n] + distance[n][i]; predecessor[i] = n; } } }
Best_Path (continued) // At this point we know predecessor of each node on the // best path from Start to Dest best_path.push_front(Dest); int next_node_id = Dest_ID; while (next_node_id != Start_ID) { next_node_id = predecessor[next_node_id]; best_path.push_front(nodes[next_node_id]); } return best_path; }
main.cpp void Show_Best_Path(string Start, string Dest) { deque<string> best_path = connections.Best_Path(Start, Dest); if (best_path.size() == 0) { cout << "No path found\n"; } else { cout << "Best path:\n"; while (best_path.size() > 0) { string next = best_path.front(); best_path.pop_front(); cout << next << endl; } } cout << endl; }
main.cpp connections.Display(); cout << endl; while (true) { string start; string dest; cout << "Start: "; getline(cin, start); cout << "Destination: "; getline(cin, dest); Show_Best_Path(start, dest); } cin.get(); return 0; }