4
\$\begingroup\$

I have implemented Dijkstra's algorithm using a slightly modified version of the structure and class posted here. Unfortunately, I have ruined the efficiency. I am intent on using this structure. I will NOT use BOOST. Any STL algorithms are acceptable.

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <list>
#include <set>
#include <algorithm>
struct vertex {
 typedef std::pair<double, vertex*> vert; // weight, destination pair
 std::vector<vert> adj; // vector holding <weight of edge, destination vertex>
 std::string name; // to hold name/title of vertex
 vertex(std::string str) : name(str) {} // struct constructor pass name to vertex.name
};
class graph {
 typedef std::map<std::string, vertex *> vertmap; // name, vertex pair
 vertmap port; // map holding <name of vertex, pointer to vertex>
 std::vector<std::string> travel; // vector holds BFS, DFS, or shortest distance
 typedef std::pair<std::string, bool> visited; // visited name, bool pair
 void depthFirstUtil(const std::string&); // helper for depth first search
 vertex* addvertex(const std::string&); // add a vertex to the graph
public:
 graph() { }
 graph(std::vector<vertex*> edges);
 void addedge(const std::string&, const std::string&, const double); // add a weighted edge to the graph
 std::vector<std::string> getDepthFirst(const std::string&);
 std::vector<std::string> getBredthFirst(const std::string&);
 typedef std::pair<double, std::string> dPair;
 std::vector<dPair> getShortestDistance(const std::string&);
};
int main() {
 graph mygraph;
 std::string myverts[6] = { "v0", "v1", "v2", "v3", "v4", "v5" };
 mygraph.addedge(myverts[0], myverts[1], 2);
 mygraph.addedge(myverts[0], myverts[5], 9);
 mygraph.addedge(myverts[1], myverts[2], 8);
 mygraph.addedge(myverts[1], myverts[3], 15);
 mygraph.addedge(myverts[1], myverts[5], 6);
 mygraph.addedge(myverts[2], myverts[3], 1);
 mygraph.addedge(myverts[4], myverts[2], 7);
 mygraph.addedge(myverts[4], myverts[3], 3);
 mygraph.addedge(myverts[5], myverts[4], 3);
 std::cout << "Depth first: ";
 for (auto ver : mygraph.getDepthFirst(myverts[0])) std::cout << ver << " ";
 std::cout << std::endl << std::endl << "Bredth first: ";
 for (auto ver : mygraph.getBredthFirst(myverts[0])) std::cout << ver << " ";
 std::cout << std::endl << std::endl << "Shortest distance: " << std::endl;
 for (auto ver : mygraph.getShortestDistance(myverts[0])) std::cout << ver.second << " " << ver.first << std::endl;
 system("pause");
 return 0;
}
graph::graph(std::vector<vertex*> edges) {
 for (auto edge : edges) for (auto dest : edge->adj) addedge(edge->name, dest.second->name, dest.first);
}
void graph::depthFirstUtil(const std::string& inName) {
 travel.push_back(inName); // mark inName as visited
 std::vector<vertex::vert> avec = port.at(inName)->adj; // Recur for all the vertices adjacent to this vertex
 for (auto i : avec) 
 if (std::find(travel.begin(), travel.end(), i.second->name) == travel.end()) 
 depthFirstUtil(i.second->name);
}
std::vector<std::string> graph::getDepthFirst(const std::string& begin) { 
 travel.clear(); // Mark all the vertices as not visited
 depthFirstUtil(begin); // Call the recursive helper function to print DFS traversal
 return travel;
}
std::vector<std::string> graph::getBredthFirst(const std::string& name) {
 travel.clear();
 std::list<std::string> queue; // Create a queue for BFS
 queue.push_back(name);
 while (!queue.empty()) {
 travel.push_back(queue.front()); // Dequeue a vertex from queue and store it
 queue.pop_front();
 for (auto i : port.at(travel.back())->adj) // Get all adjacent vertices of the dequeued vertex 
 if((std::find(travel.begin(), travel.end(), i.second->name) == travel.end()) // If an adjacent vertex has not been visited, then enqueue it
 && (std::find(queue.begin(), queue.end(), i.second->name) == queue.end())) // IF NOT IN TRAVEL AND NOT IN QUEUE!
 queue.push_back(i.second->name);
 }
 return travel;
}
vertex* graph::addvertex(const std::string &name) {
 vertmap::iterator itr = port.find(name);
 if (itr == port.end()) {
 vertex *v = new vertex(name);
 port[name] = v;
 return v;
 }
 else return itr->second;
}
void graph::addedge(const std::string& from, const std::string& to, const double weight) {
 vertex *f = (addvertex(from));
 vertex *t = (addvertex(to));
 std::pair<double, vertex *> edge = std::make_pair(weight, t);
 f->adj.push_back(edge);
}
typedef std::pair<double, std::string> dPair;
std::vector<dPair> graph::getShortestDistance(const std::string& start) {
 travel.clear();
 std::vector<dPair> nameDist;
 std::vector<dPair> nameDistCopy;
 std::string vertName;
 std::set<vertex*> queue;
 double totDist;
 for (auto i : port) {
 dPair portPair = std::make_pair(INFINITY, i.first);
 nameDist.push_back(portPair);
 nameDistCopy.push_back(portPair);
 queue.insert(i.second);
 }
 auto srcItr = std::find_if(nameDist.begin(), nameDist.end(), [=](const dPair vName) {
 return vName.second == start;
 });
 double minDist = (*srcItr).first = 0;
 while (!queue.empty()) {
 auto vNameItr = std::min_element(nameDistCopy.begin(), nameDistCopy.end());
 vertName = (*vNameItr).second;
 minDist = (*std::find_if(nameDist.begin(), nameDist.end(), [=](const dPair element) {
 return element.second == vertName;
 })).first;
 auto qVertItr = std::find_if(queue.begin(), queue.end(), [=](const vertex* element) {
 return element->name == vertName;
 });
 vertex *minVert;
 minVert = *qVertItr;
 queue.erase(minVert);
 for (auto neighbor : minVert->adj) {
 totDist = minDist + neighbor.first;
 auto distItr = std::find_if(nameDist.begin(), nameDist.end(), [=](const dPair vName) {
 return vName.second == neighbor.second->name;
 });
 if (totDist < (*distItr).first) {
 (*distItr).first = totDist;
 }
 }
 auto erItr = std::find_if(nameDistCopy.begin(), nameDistCopy.end(), [=](const dPair vName) {
 return vName.second == minVert->name;
 });
 nameDistCopy.erase(erItr);
 }
 return nameDist;
}

I would very much appreciate any/all optimization of the getShortestDistance function. I am fine with the implementation of the other functions.

asked Dec 10, 2016 at 21:49
\$\endgroup\$
2
  • \$\begingroup\$ I am intent on using this structure Which part of program or data structure(s) does this refer to? (I conclude it does not to getShortestDistance ().) \$\endgroup\$ Commented Dec 11, 2016 at 12:28
  • \$\begingroup\$ It refers to the structure defined in the class. struct vertex {...}; \$\endgroup\$ Commented Dec 16, 2016 at 19:08

2 Answers 2

1
\$\begingroup\$

Yes, your implementation of Dijkstra's shortest path algorithm is far from efficient. Here is a pseudo code for a better one:

DijkstraSearch(graph g, int source, int target):
 OPEN = std::priority_queue<int>
 CLOSED = std::unordered_set<int>
 parent_map = std::unordered_map<int, int>
 distance_map = std::unordered_map<int, double>
 OPEN.push(source)
 parent_map[source] = source
 distance_map[source] = 0.0
 while (OPEN not empty):
 current = OPEN.extractMinimum()
 if current is target:
 return traceback_path(source, target, parent_map)
 if CLOSED contains current:
 continue
 CLOSED.add(current)
 for neighbor in graph.neighbors_of(current):
 if CLOSED contains neighbor:
 continue
 double tentative_distance = distance_map[current] + graph.weight(current, neighbor)
 if neighbor not in parent_map.keys() or tentative_distance < distance_map[neighbor]:
 distance_map[neighbor] = tentative_score
 parent_map[neighbor] = current
 OPEN.insert(neighbor, tentative_distanse) # insert(node, priority)
 error "No path"
traceback_path(source, target, parent_map):
 path = []
 current = target
 while (true):
 path.append(current)
 current = parent_map[current]
 if current == source:
 break
 path.reverse()
 return path

If implemented correctly, the above will run in \$\mathcal{O}((m + n) \log n)\$. If you, however, use a Fibonacci heap, the running time will improve to \$\mathcal{O}(m + n \log n)\$.

answered Dec 11, 2016 at 8:31
\$\endgroup\$
0
\$\begingroup\$

Actually, the algorithm posted by op (me) fails to function properly. If you were to input "v5" as the starting point, the only results would be the distances from "v5" to "v5" == 0, and "v5" to "v4" == 3. The intent of the algorithm (in this case) is to return the shortest distance from some arbitrary starting vertex to each vertex in the graph. To remedy this only (does NOT improve efficiency), the starting vertex should be set to 0 in both the nameDist pair and the nameDistCopy pair. Further, when the total distance is less than the currently stored nameDist pair it should be updated in both nameDist and nameDistCopy.

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
answered Dec 11, 2016 at 18:37
\$\endgroup\$
0

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.