The goal of the semester project is be sure you can create a C++ program that follows a specification, implements an algorithm, reuses library and other classes and has a low defect density.
Write a program in Standard C++ that reads a weighted directed graph, computes and prints the shortest weighted distance from the first vertex to all other vertices. The Standard C++ program must stay within the 'constraints' given below as comments in the file "dijkstra.cpp" The project must be submitted on gl.umbc.edu using submit cs109 project dijkstra.cpp (and any other files you need) Use the file extension .cpp for grading using Visual C++ 6.0 Use the file extension .C for grading on gl using CC -n32 -Iinclude Use the file extension .cc for grading on gl using g++ (errors about weak bindings will be ignored) Sample "structure" of main program: // dijkstra.cpp CMSC 109 Spring 1999 project definition // // Given a connected directed graph as input (a set of weighted edges) // Read in the graph and compute the shortest weighted distance // from the source (first vertex in the input data) to every other vertex. // Print the list of vertices in the order read, one per line, // followed by one space and the distance from the source. // // Constraints: // Use good Standard C++ style // Use C++ STL as much as possible. // Do use any library header files of the form <*.h> // You may define your own header files and include them "*.h" // Justify, by writing a paragraph, if you use any include files //that are C library header files for Standard C++ // // Hints: Suggested but not required. You can do it your way. // You may use graph_read.cpp and edit it to suit your needs. // You may want to put your data structure definitions in // a header file, "graph.h" included below, would be such a file. // #include <iostream> using namespace std; #include "graph.h" // or have everything in one file int main(int argc, char *argv[]) // standard main program definition { graph_type G; // The graph is stored in the object G graph_type::iterator vert; // need an iterator through G Adj temp; // need a temporary Adj list Adj::iterator p; // need a temporary iterator through temp cout << "dijkstra running" << endl; // trace execution if(argc<2) // be sure user supplied a file name { cout << "no file on command line. Exiting." << endl; return 0; } graph_input(argv[1], G); // input the graph data from file // make a function or use graph_read.cpp // initialize each vertex to not done for(vert=G.begin(); vert!=G.end(); vert++) vert->set_done(false); // pseudo code for Dijkstra's shortest path algorithm (must make this C++) // while some vertex not done // do u <- vertex with smallest distance that is not done // for each edge v in u's adjacency list // do if v's distance is greater than u's distance + v's weight // then v's distance becomes u's distance + v's weight // mark u done // print shortest path result for(vert=G.begin(); vert!=G.end(); vert++) cout << vert->get_vertex() << " " << vert->get_distance() << endl; cout << endl; cout << "dijkstra finished" << endl; return 0; }
The input is a graph. The graph is defined by weighted edges. Each line has a vertex1, a vertex2 and an integer weight. Sample input: dijkstra1.dat Sample code to read input into a vector of lists data structure: graph_read.cpp Then the output of graph_read.cpp shows the data structure that is built: graph_read.out The above was compiled and executed using g++ -o graph_read graph_read.cpp graph_read dijkstra1.dat > graph_read.out For g++ on gl machines (or IRIX 6.5.3 there is a problem with fstream) Thus use graph_read1.cc source code and compile g++ -o graph_read1 graph_read1.cc and execute graph_read1 < dijkstra1.dat > graph_read1.out
See "Project Testing" below.
Implement Dijkstra's Shortest Path Algorithm using an adjacency list data structure. (The sample code "graph_read.cpp" referenced above builds one possible adjacency list data structure.) A graph presented as a diagram looks like:where a circle represents a vertex and a line represents an edge. The number on the edge line is called a weight. The computer input data is easily represented by three items on a line for each edge: vertex1 vertex2 weight given with data types string string int respectively in C++. S U 10 S X 5 U V 1 U X 2 V Y 4 X U 3 X V 9 X Y 2 Y S 7 Y V 6 A graph can be stored in a computer as a data structure called an adjacency list. The initial data structure for the data above is:
Algorithm: Dijkstra's Shortest Path applied to the data above: find a pointer u to a not done, F, vertex in G that is the smallest (u points to [S 0 F] for this graph) for each edge in u's adjacency list, get a pointer v to G (our first v points to [U 10000 F], v actually contains "&U") if v's distance is greater than u's distance + v's weight then set v's distance to u's distance + v's weight. (our case if 10000 > 0 + 10 then 10000 replaced by 0 + 10) set u done. (our case [S 0 F] becomes [S 0 T] ) G after 1st pass G after 2nd pass G after 3rd pass G after 4th pass +--------------+ +--------------+ +--------------+ +--------------+ | S 0 T | | S 0 T | | S 0 T | | S 0 T | | U 10 F | | U 8 F | | U 8 F | | U 8 T | | X 5 F | | X 5 T | | X 5 T | | X 5 T | | V 100000 F | | V 14 F | | V 13 F | | V 9 F | | Y 100000 F | | Y 7 F | | Y 7 T | | Y 7 T | +--------------+ +--------------+ +--------------+ +--------------+
A starter set of test files and correct results is provided. Add tests to these to be sure the specification is met. dijkstra1.dat input file results in dijkstra1.out as an output file. From running dijkstra dijkstra1.dat > dijkstra1.out dijkstra2.dat input file results in dijkstra2.out as an output file. From running dijkstra dijkstra2.dat > dijkstra2.out dijkstra3.dat input file results in dijkstra3.out as an output file. From running dijkstra dijkstra3.dat > dijkstra3.out You should print the input file name and the input data, the debug print showing the data structure may be deleted. The results MUST be printed.
DO NOT LEAVE THIS TO THE LAST DAY! Expect to spend 16 hours of studing the problem and very careful programming on this project. (It will take 40 hours if you are sloppy or careless.) Comment your code as much for yourself as for grading. Use indentation to match the structure of your code. You can leave debug print in your code. It is more professional to have debug print under the control of something to turn it off.
Points can be lost by: Partially incorrect results, Lack of comments to follow what code is doing at a coarse level. Really bad, Standard C++ programming practice. Partial credit will be given for partially working code. Code must compile, link and get some amount of output for partial credit. NOTE: not working with g++ on gl machines Spring 1999, use graph_read1.cc ! // graph_read.cpp get file name, open file, read file #include <iostream> // console I/O #include <fstream> // file I/O #include <string> // for input data #include <algorithm> #include <list> #include <vector> using namespace std; // bring standard names into scope class my_vertex; // just declaring my_vertex as a type class my_edge // the type of objects in the adjacency lists { public: my_edge(string Aedge, int Aweight){edge=Aedge; weight=Aweight;} inline string get_edge(){ return edge;} inline my_vertex * get_vertex_ptr(){return vertex;} inline void set_vertex_ptr(my_vertex * ptr){vertex=ptr;} inline int get_weight(){ return weight;} private: // these data items are for the adjacency list string edge; // name of vertex (edge is from parent vertex to here) my_vertex * vertex; // pointer to this vertex in the vector int weight; // weight of this edge }; typedef list<my_edge> Adj; // the type of each adjacency list class my_vertex // the type of objects in the vector of vertices { public: my_vertex(string Avertex, int d) {vertex=Avertex; distance=d;} my_vertex(string Avertex, int d, my_edge Aedge) {vertex=Avertex; distance=d; edge_list.push_back(Aedge);} inline int get_distance(){return distance;} inline string get_vertex(){return vertex;} inline Adj get_edges(){return edge_list;} inline Adj * get_Adj_ptr(){return &edge_list;} inline void add_edge(string vert2, int weight) {edge_list.push_back(my_edge(vert2, weight));} inline void set_distance(int d){distance=d;} inline bool exist(string s){return vertex==s;} inline bool is_done(){return done;} inline void set_done(bool state){done=state;} private: // these data items are for each vertex string vertex; // name of this vertex int distance; // distance of this vertex from source vertex bool done; // finished with this vertex // other variables could go here for more applications Adj edge_list; // the head of the edge list for this vertex }; typedef vector<my_vertex> graph_type; // type of the graph object typedef graph_type::iterator graph_iterator; // type of iterator for vector // probably best if the above was in a header file and #include here int main(int argc, char *argv[]) // standard main program definition { fstream my_io; // a users variable that holds stream information string vert1; // just a place for inputting string vert2; // " int weight; // " graph_type G; // The graph is stored in the object G graph_iterator vert; // need an iterator through G graph_iterator vert_p2; // another iterator through G Adj temp; // need a temporary Adj list Adj::iterator p; // need a temporary iterator through temp bool found; // temporary cout << "graph_read running" << endl; //trace execution if(argc<2) // be sure user supplied a file name { cout << "no file on command line. Exiting." << endl; return 0; } cout << "about to open " << argv[1] << endl; // just informative my_io.open(argv[1], ios::in); // open file if(!my_io) // believe it!, anything can go wrong { cout << "can not open file " << argv[1] << endl; // common error message return 1; // common error exit } while(!my_io.eof()) // loop until end of file { my_io >> vert1; if(vert1==string("#") || vert1==string("") ) // ignore comments and empty { my_io.ignore(100, '\n'); continue; } my_io >> vert2 >> weight; // read the other two fields on the line cout << vert1 << " " << vert2 << " " << weight << endl; //print what read // if vert1 not a vertex, construct it if(G.empty()) // special { G.push_back(my_vertex(vert1, 0, my_edge(vert2, weight))); G.push_back(my_vertex(vert2, 10000)); // no Adj yet } else { // find and push_back if vert==G.end() for vert2 and vert1 found = false; // look for vert2 in vector of vertices for(vert=G.begin(); vert!=G.end(); vert++) { if(vert->exist(vert2)) { found = true; break; } } if(!found) { // if vert2 not a vertex, construct it G.push_back(my_vertex(vert2, 10000)); // no Adj } found = false; // look for vert1 in vector of vertices for(vert=G.begin(); vert!=G.end(); vert++) { if(vert->exist(vert1)) { vert->add_edge(vert2, weight); found = true; break; } } if(!found) { // if vert1 not a vertex, construct it G.push_back(my_vertex(vert1, 10000, my_edge(vert2, weight))); } } // end if for 'else' } // end of while not eof // OK, G vector will not be reallocated again, // fill in Adj to vector pointers for(vert=G.begin(); vert!=G.end(); vert++) { for(p=vert->get_Adj_ptr()->begin(); p!=vert->get_Adj_ptr()->end(); p++) { vert2 = p->get_edge(); for(vert_p2=G.begin(); vert_p2!=G.end(); vert_p2++) { if(vert_p2->exist(vert2)) { p->set_vertex_ptr(vert_p2); break; } } } } cout << "print out data structure G" << endl; cout << "G.begin(), G.end() " << G.begin() << ' ' << G.end() << endl; for(vert=G.begin(); vert!=G.end(); vert++) { cout << vert->get_vertex() << " " << vert->get_distance() << " " << vert << endl; temp = vert->get_edges(); cout << " "; // use begin() end() to be safe for(p=temp.begin(); p!=temp.end(); p++) cout << '(' << p->get_edge() << ',' << p->get_vertex_ptr() << ',' << p->get_weight() << ") "; cout << endl; } my_io.close(); // just being neat, closing file being read cout << "graph_read finished" << endl; return 0; } NOTE: works around bug inin g++ on gl g++ -o graph_read1 graph_read1.cc graph_read1 < dijkstra1.dat // graph_read1.cc graph_read1 < dijkstra1.dat > dijkstra1.chk // work around for g++ on UMBC9 (IRIX 6.5.3) // segmentation fault on my_io >> vert1 problem #include <iostream> // console I/O #include <string> // for input data #include <algorithm> #include <list> #include <vector> using namespace std; // bring standard names into scope class my_vertex; // just declaring my_vertex as a type class my_edge // the type of objects in the adjacency lists { public: my_edge(string Aedge, int Aweight){edge=Aedge; weight=Aweight;} inline string get_edge(){ return edge;} inline my_vertex * get_vertex_ptr(){return vertex;} inline void set_vertex_ptr(my_vertex * ptr){vertex=ptr;} inline int get_weight(){ return weight;} private: // these data items are for the adjacency list string edge; // name of vertex (edge is from parent vertex to here) my_vertex * vertex; // pointer to this vertex in the vector int weight; // weight of this edge }; typedef list<my_edge> Adj; // the type of each adjacency list class my_vertex // the type of objects in the vector of vertices { public: my_vertex(string Avertex, int d) {vertex=Avertex; distance=d;} my_vertex(string Avertex, int d, my_edge Aedge) {vertex=Avertex; distance=d; edge_list.push_back(Aedge);} inline int get_distance(){return distance;} inline string get_vertex(){return vertex;} inline Adj get_edges(){return edge_list;} inline Adj * get_Adj_ptr(){return &edge_list;} inline void add_edge(string vert2, int weight) {edge_list.push_back(my_edge(vert2, weight));} inline void set_distance(int d){distance=d;} inline bool exist(string s){return vertex==s;} inline bool is_done(){return done;} inline void set_done(bool state){done=state;} private: // these data items are for each vertex string vertex; // name of this vertex int distance; // distance of this vertex from source vertex bool done; // finished with this vertex // other variables could go here for more applications Adj edge_list; // the head of the edge list for this vertex }; typedef vector<my_vertex> graph_type; // type of the graph object typedef graph_type::iterator graph_iterator; // type of iterator for vector // probably best if the above was in a header file and #include here int main(int argc, char *argv[]) // standard main program definition { string vert1; // just a place for inputting string vert2; // " int weight; // " graph_type G; // The graph is stored in the object G graph_iterator vert; // need an iterator through G graph_iterator vert_p2; // another iterator through G Adj temp; // need a temporary Adj list Adj::iterator p; // need a temporary iterator through temp bool found; // temporary cout << "graph_read1 running" << endl; //trace execution while(!cin.eof()) // loop until end of file { cin >> vert1; if(vert1==string("#") || vert1==string("") ) // ignore comments and empty { cin.ignore(100, '\n'); continue; } cin >> vert2 >> weight; // read the other two fields on the line cout << vert1 << " " << vert2 << " " << weight << endl; //print what read // if vert1 not a vertex, construct it if(G.empty()) // special { G.push_back(my_vertex(vert1, 0, my_edge(vert2, weight))); G.push_back(my_vertex(vert2, 10000)); // no Adj yet } else { // find and push_back if vert==G.end() for vert2 and vert1 found = false; // look for vert2 in vector of vertices for(vert=G.begin(); vert!=G.end(); vert++) { if(vert->exist(vert2)) { found = true; break; } } if(!found) { // if vert2 not a vertex, construct it G.push_back(my_vertex(vert2, 10000)); // no Adj } found = false; // look for vert1 in vector of vertices for(vert=G.begin(); vert!=G.end(); vert++) { if(vert->exist(vert1)) { vert->add_edge(vert2, weight); found = true; break; } } if(!found) { // if vert1 not a vertex, construct it G.push_back(my_vertex(vert1, 10000, my_edge(vert2, weight))); } } // end if for 'else' } // end of while not eof // OK, G vector will not be reallocated again, // fill in Adj to vector pointers for(vert=G.begin(); vert!=G.end(); vert++) { for(p=vert->get_Adj_ptr()->begin(); p!=vert->get_Adj_ptr()->end(); p++) { vert2 = p->get_edge(); for(vert_p2=G.begin(); vert_p2!=G.end(); vert_p2++) { if(vert_p2->exist(vert2)) { p->set_vertex_ptr(vert_p2); break; } } } } cout << "print out data structure G" << endl; cout << "G.begin(), G.end() " << G.begin() << ' ' << G.end() << endl; for(vert=G.begin(); vert!=G.end(); vert++) { cout << vert->get_vertex() << " " << vert->get_distance() << " " << vert << endl; temp = vert->get_edges(); cout << " "; // use begin() end() to be safe for(p=temp.begin(); p!=temp.end(); p++) cout << '(' << p->get_edge() << ',' << p->get_vertex_ptr() << ',' << p->get_weight() << ") "; cout << endl; } // PUT PROJECT CODE HERE cout << "graph_read1 finished" << endl; return 0; }
Last updated 4/7/99