/* * Code file for Graph class model solution for CS 202, November 2002. * Orgainization: Member functions of Graph are first, then class * iterator, then fancy iterators (dfs). */ #include #include "Graph.hh" // If forms an edge in this Graph, returns the weight of // that edge has been returned. Otherwise, WEIGHT_NOT_FOUND is returned. // Wild compiler note: This member function wants to be const, and // for it to work, compiler demands that itr at line 1 be a // const_iterator, and that in turn forces list_itr to be a const_iterator. Weight Graph::edgeWeight (const VertexName& v1, const VertexName& v2) const { // vmapIterator itr = vertexMap.find (v1); vmap::const_iterator itr = vertexMap.find (v1); if (itr == vertexMap.end() || vertexMap.find (v2) == vertexMap.end()) return WEIGHT_NOT_FOUND; list::const_iterator list_itr; for (list_itr = ((*itr).second).begin(); list_itr != ((*itr).second).end(); list_itr++) if ((*list_itr).to == v2) return (*list_itr).wt; return WEIGHT_NOT_FOUND; // v1 & v2 both in graph, but no edge! } // Returns true if this Graph contains the edge . Otherwise, false. bool Graph::containsEdge (const VertexName& v1, const VertexName& v2) const { list::const_iterator list_itr; vmap::const_iterator itr = vertexMap.find (v1); if (itr == vertexMap.end() || vertexMap.find (v2) == vertexMap.end()) return false; for (list_itr = ((*itr).second).begin(); list_itr != ((*itr).second).end(); list_itr++) if ((*list_itr).to == v2) return true; return false; } // method containsEdge bool Graph::containsVertex (const VertexName& v) const { return vertexMap.find(v) != vertexMap.end(); } // We look up v with find and return a list of its negihbors. // Notes: Need const_iterator since function is const; we must walk // through the map, since we're to return a list only of VertexName, // not of both VertexName and weight in a struct. list Graph::getNeighbors(const VertexName& v) const { vmap::const_iterator itr = vertexMap.find(v); assert (itr != vertexMap.end()); // v is supposed to be in the Graph! list answerList; list::const_iterator listItr; for (listItr = ((*itr).second).begin(); listItr != ((*itr).second).end(); listItr++) answerList.push_back( (*listItr).to); return answerList; } // Adds edge to graph. Probably blows up the world if the // graph already has cuz prof. made assignment to easy and // didn't ask for error checking. void Graph::insertEdge (const VertexName& v1, const VertexName& v2, Weight w) { // More reasonable design specs would do something like: // if (containsEdge (v1, v2)) // return false; insertVertex (v1); insertVertex (v2); (*(vertexMap.find (v1))).second.push_back(VertexAndWeight (v2, w)); } // Returns false if v is already in this Graph, otherwise returns true // and adds vertex v with empty adjacency list to the graph. bool Graph::insertVertex (const VertexName& v) { const list emptyAdjList; vpair newEntry = vpair(v, emptyAdjList); return (vertexMap.insert(newEntry)).second; } // Returns the number of edges in this graph -- counts 'em! unsigned int Graph::edgeCount() const { int count = 0; vmap::const_iterator itr; for (itr = vertexMap.begin(); itr != vertexMap.end(); itr++) count += (*itr).second.size(); return count; } // Assume v is in this Graph. Returns a dfs_reach_iterator over all // vertices reachable from v. Graph::dfs_reach_iterator Graph::dfs_reach_begin (const VertexName& v) { dfs_reach_iterator dfsItr (v, vertexMap); return dfsItr; } // Returns a dfs_reach_iterator that can be used in comparisons to // terminate this iteration of this DFS of the Graph. Graph::dfs_reach_iterator Graph::dfs_reach_end() { dfs_reach_iterator dfsItr; // Initializes class-members to empty ones dfsItr.atEnd = true; return dfsItr; } // Assume v is in this Graph. Returns a dfs_all_iterator over all // vertices reachable from v. Graph::dfs_all_iterator Graph::dfs_all_begin (const VertexName& v) { dfs_all_iterator dfsItr (v, vertexMap); return dfsItr; } // Returns a dfs_all_iterator that can be used in comparisons to // terminate this iteration of this DFS of the Graph. Graph::dfs_all_iterator Graph::dfs_all_end() { dfs_all_iterator dfsItr; // Initializes class-members to empty ones dfsItr.atEnd = true; return dfsItr; } /* * Definitions for class iterator here: */ // Return type is just next vertex name; seems easy for client to use. VertexName Graph::iterator::operator*() const { return (*theItr).first; } // Moves iterator forward one position; returns previous VERTEXNAME. VertexName Graph::iterator::operator++ (int) { VertexName returnVertex = (*theItr).first; // current vertex name theItr++; // non-coward would pick name from this! return returnVertex; } bool Graph::iterator::operator==(const iterator& rhs) const { return theItr == rhs.theItr; } bool Graph::iterator::operator!=(const iterator& rhs) const { return theItr != rhs.theItr; } /* * Definitions for class dfs_reach iterator here: */ //Private 2 element constructor that initializes iterator to start vertex // Relies on fact that since visited is a map (of class type) it will // be initialized to the empty map. Graph::dfs_reach_iterator::dfs_reach_iterator (const VertexName& start, vmap g): graphMap(g), atEnd(false) { // Mark each VertexName as not visited: vmap::iterator itr; for (itr = graphMap.begin(); itr != graphMap.end(); itr++) visited[(*itr).first] = false; visited[start] = true; dfsStack.push (start); } // two-parameter constructor // I'm using atEnd bool flag to say when we've reached the end, and // checking only that. A more sophisticated apprach would need to // think about what is Right Thing when you have dfs_reach_iterator's // for two DIFFERENT GRAPHS, both at end. I'm just // unilateraly declaring them equal, which is probably wrong thing, // but hey, I'm documenting it!! bool Graph::dfs_reach_iterator::operator==(const dfs_reach_iterator& rhs) const { if (atEnd || rhs.atEnd) return atEnd == rhs.atEnd; return (dfsStack == rhs.dfsStack) && (graphMap == rhs.graphMap) && visited == rhs.visited; } bool Graph::dfs_reach_iterator::operator!=(const dfs_reach_iterator& rhs) const { return ! ((*this) == rhs); } // Precondition: this dfs_reach_iterator has not yet // visited all of the reachable vertices in this // Graph. // Postcondition: this dfs_reach_iterator has been advanced // to the next reachable VertexName in this Graph; // the pre-advanced iterator has been returned. Graph::dfs_reach_iterator Graph::dfs_reach_iterator::operator++ (int) { assert (! atEnd); // That's what precondition means! dfs_reach_iterator temp = *this; VertexName current = dfsStack.top(); dfsStack.pop(); // Stupid STL pop has VOID return type! vmap::iterator itr = graphMap.find (current); list::iterator list_itr; for (list_itr = (*itr).second.begin(); list_itr != (*itr).second.end(); list_itr++) { VertexName to = (*list_itr).to; if ( !(visited[to]) ) { visited[to] = true; dfsStack.push (to); } // if } // for if (dfsStack.empty()) // Done; turn this into empty one atEnd = true; return temp; } // operator++ /* * Definitions for class dfs_all iterator here: */ //Private 2 element constructor that initializes iterator to start vertex // Relies on fact that since visited is a map (of class type) it will // be initialized to the empty map. Graph::dfs_all_iterator::dfs_all_iterator (const VertexName& start, vmap g): graphMap(g), atEnd(false) { // Mark each VertexName as not visited: vmap::iterator itr; for (itr = graphMap.begin(); itr != graphMap.end(); itr++) visited[(*itr).first] = false; visited[start] = true; dfsStack.push (start); } // two-parameter constructor // I'm using atEnd bool flag to say when we've alled the end, and // checking only that. A more sophisticated apprach would need to // think about what is Right Thing when you have dfs_all_iterator's // for two DIFFERENT GRAPHS, both at end. I'm just // unilateraly declaring them equal, which is probably wrong thing, // but hey, I'm documenting it!! bool Graph::dfs_all_iterator::operator==(const dfs_all_iterator& rhs) const { if (atEnd || rhs.atEnd) return atEnd == rhs.atEnd; return (dfsStack == rhs.dfsStack) && (graphMap == rhs.graphMap) && visited == rhs.visited; } bool Graph::dfs_all_iterator::operator!=(const dfs_all_iterator& rhs) const { return ! ((*this) == rhs); } // This is where the only difference from the reach operator is. // Before we declare we are done, we must check that all have been // visited, not just that the stack is empty. And we must handle the // case of going forward when the stack is empty but not all have been // visited. The solution given below is mildly inefficient, in that // it searches all vertices for an unvisited one each time we've // gotten through one component of the graph. More efficient // algorithm would be to add a class data member to remember where we // are in the iterator though all vertices when we need to check this. Graph::dfs_all_iterator Graph::dfs_all_iterator::operator++ (int) { assert ( !atEnd); // That's what precondition means! dfs_all_iterator temp = *this; VertexName current = dfsStack.top(); dfsStack.pop(); // Stupid STL pop has VOID return type! vmap::iterator itr = graphMap.find (current); list::iterator list_itr; for (list_itr = (*itr).second.begin(); list_itr != (*itr).second.end(); list_itr++) { VertexName to = (*list_itr).to; if ( !(visited[to]) ) { visited[to] = true; dfsStack.push (to); } // if } // for if (dfsStack.empty()) // Look through all vertices for unvisited vertex. If found, visit // it. If none found, indicate iterator is now at end. { itr = graphMap.begin(); while ( itr != graphMap.end() && visited[(*itr).first]) ++itr; if (itr == graphMap.end()) atEnd = true; else { VertexName nextVertex = (*itr).first; dfsStack.push(nextVertex); visited[nextVertex] = true; } } return temp; }// operator++