Reputation: 341
im trying to find the shortest path from the vertex to another. To be more precise I have a directed graph and by going always "forward" in it i will always end up in the end. Something like structure of neural network. I decided to find the shortest way with recursion which worked perfectly fine with smaller numbers. But for bigger data I get the SIGSEGV. I almost sure it's the stack overflow. Do any of you have any idea how I can switch from simple recurrence to something that wont cause the trouble?
int findShortestPath(Vertex * v, int endPointX){
if(v->isShortestPathSet())
return v->getShortestPath();
vector<int> * paths = new vector<int>;
if(v->getEndPos() == endPointX)
return 0;
for(int i = 0; i < v->getOutputEdges().size(); i ++){
Edge * outputEdge = v->getOutputEdges().at(i);
paths->push_back(findShortestPath(outputEdge->getOutputVertex(), endPointX) + outputEdge->getValue());
}
int minPath = paths->at(0);
for(int i = 0; i < paths->size(); i ++){
if(paths->at(i) < minPath)
minPath = paths->at(i);
}
v->setShortestPath(minPath);
free(paths);
return minPath;
}
this is the function with which im looking for the shortest path. It momorises the shortest possible path to each vertex so in further queries i wont have to repeat these expensive calculations.
Upvotes: 3
Views: 185
Reputation: 5457
You can implement the Dijkstra's algorithm iteratively. Here's a snippet of code which implements Dijkstra's algorithm iteratively
#include <queue>
#include <unordered_map>
#include <vector>
using IntPair = std::pair<int,int>;
std::priority_queue<IntPair, std::vector<IntPair>, std::greater<IntPair>> pq;
std::unordered_map<int, std::unordered_map<int, int>> g;
std::vector<int> distance, parent;
void dijkstras(int startVertex) {
// insert the startVertex into the priority queue(pq)
pq.push(std::make_pair(0, startVertex));
while (!pq.empty()) {
// select the vertex with least distance travelled so far from the pq
// and then, pop the selected vertex from pq
auto [dist, src] = pq.top(); pq.pop();
// iterate on all its neighbours and update distance[] and parent[]
for (auto [v, weight] : g[src]) {
if (int newDist = dist + weight; newDist < distance[v]) {
parent[v] = src;
distance[v] = newDist;
pq.push(std::make_pair(newDist, v));
}
}
}
}
Here,
pq
is a priority queue which stores pairs of (distanceTravelledSoFar, previousNode)
. Here, pq
acts as a min heap which helps us to choose the next node optimallyg
is just an adjacency list that you use to stores the graphdistance
is array of the shortest path distances to each of the vertex from startVertex
parent
is the array which stores the previous node in the shortest path to each vertex from startVertex
Here's the link to the code which I have used to solve this question
Upvotes: 3
Reputation: 11281
An answer to your question is suggested in the comments (and Cherubim gives a good example of Dijkstra's algoritm.
I will also answer by modifying you code. Firstly, I think getters and setters are not necessary and you should use modern C++. Therefore I've modified your code as follows:
#include <vector>
#include <algorithm>
#include <optional>
class Vertex;
struct Edge {
Vertex* const outputVertex;
int const value;
};
struct Vertex {
int const endPoint;
std::vector<Edge const*> const outputEdges;
std::optional<int> shortestPath;
};
int findShortestPath(Vertex* const v, int endPoint){
if(v->endPoint == endPoint) return 0;
if(v->shortestPath.has_value()) return v->shortestPath.value();
auto const& outputEdges = v->outputEdges; // hopefully prevent one layer of indirection
std::vector<int> paths; paths.reserve(outputEdges.size());
std::transform(cbegin(outputEdges), cend(outputEdges), back_inserter(paths),
[endPoint] (Edge const* const outputEdge) {
return findShortestPath(outputEdge->outputVertex, endPoint) + outputEdge->value;
});
return v->shortestPath.value() = *std::min_element(cbegin(paths), cend(paths));
}
Now, to implement the stack, you have to reverse the concept you are using: instead of recursively going to the depth and returning the distance, you pass the distance forward. Together with the stack suggested in the comments, this would lead to the following code:
#include <stack>
#include <utility>
#include <climits>
int findShortestPath(Vertex const* const startVertexPtr, int endPoint) {
int minDistance = INT_MAX;
std::stack<std::pair<Vertex const*, int>> s;
s.push(std::make_pair(startVertexPtr, 0));
while(!s.empty()) {
auto [vertexPtr, distance] = s.top(); s.pop(); // structured binding
if (vertexPtr->endPoint == endPoint) {
minDistance = std::min(minDistance, distance); // end is found, see if it's path has minimum distance
continue;
}
for(Edge const* const edge : vertexPtr->outputEdges) {
s.push(std::make_pair(edge->outputVertex, distance + edge->value)); // pass the distance forward
}
}
return minDistance;
}
... but you see I'm not using Vertex::shortestPath
here, which would offer an optimization. I havent' fully checked it, but you can probably do something like this:
First I again redefine Vertex
struct Vertex {
int const endPoint;
std::vector<Edge const*> const outputEdges;
int shortestPath = INT_MAX;
};
And then:
int findShortestPath(Vertex const* const startVertexPtr, int endPoint) {
int minDistance = INT_MAX;
std::stack<std::pair<Vertex const*, int>> s;
s.push(std::make_pair(startVertexPtr, 0));
while(!s.empty()) {
auto [vertexPtr, distance] = s.top(); s.pop();
if (vertexPtr->endPoint == endPoint) {
minDistance = std::min(minDistance, distance);
continue;
}
for(Edge const* const edge : vertexPtr->outputEdges) {
Vertex& vertex = *edge->outputVertex; // hopefully one less level of indirection
auto newDistance = distance + edge->value;
if (newDistance < vertex.shortestPath) {
vertex.shortestPath = newDistance;
s.push(std::make_pair(&vertex, newDistance));
}
}
}
return minDistance;
}
But there's probably more optimizations possible.
Upvotes: 2