I'm implementing Dijkstra's algorithm for school and my code keeps messing up. I've followed the pseudo-code on Wikipedia very closely. I implement the graph with a weighted adjacency list in this form so I check neighbours by iterating through the corresponding row.
Here's my graph class, along with my vertex struct.
struct vertex
{
//constructor
vertex(size_t d_arg, size_t n_arg)
{
n = n_arg;
d = d_arg;
}
//member variables, n is name and d is distance
size_t n;
size_t d;
//overloaded operator so I can use std::sort in my priority queue
bool operator<(const vertex& rhs) const
{
return d<rhs.d;
}
};
class graph
{
public:
graph(vector<vector<size_t> > v){ ed = v;};
vector<size_t> dijkstra(size_t src);
bool dfs(size_t src);
private:
//stores my matrix describing the graph
vector<vector<size_t> > ed;
};
The function dfs implements a Depth-first Search to check if the graph's joint. I've got no problems with it. But the function dijkstra, however, gives me the wrong values. This is how it's implemented.
vector<size_t> graph::dijkstra(size_t src)
{
//a vector storing the distances to the vertices and a priority queue
vector<size_t> dist;
dist[src] = 0;
p_q<vertex> q;
//set the distance for the vertices to inphinity, since they're size_t and -1 is largest
for (size_t i = 0; i < ed.size(); i++) {
if(i!=src)
{
dist.push_back(-1);
}
//push the vertices to the priority queue
vertex node(dist[i], i);
q.push(node);
}
//while there's stuff in the queue
while(q.size())
{
//c, the current vertex, becomes the top
vertex c = q.pop();
//iterating through all the neighbours, listed in the adjacency matrix
for(int i = 0; i < ed[0].size(); i++)
{
//alternative distance to i is distance to current and distance between current and i
size_t alt = dist[c.n] + ed[c.n][i];
//if we've found a better distance
if(alt < dist[i])
{
//new distance is alternative distance, and it's pushed into the priority queue
dist[i] = alt;
vertex n(alt, i);
q.push(n);
}
}
}
return dist;
}
I can't see why I'm having trouble. I've debugged with this matrix.
0 3 -1 1
3 0 4 1
-1 4 0 -1
1 1 -1 0
And it didn't visit anything other than vertex 0 and vertex 3.
One of the problems is right at the beginning of graph::dijkstra, when an element of zero-sized array is assigned:
vector<size_t> dist;
dist[src] = 0;
It is OK in pseudo-code, but not in C++. Perhaps you may change like this:
vector<size_t> dist;
for (size_t i = 0; i < ed.size(); i++) {
if(i!=src)
{
dist.push_back(-1);
}
else
{
dist.push_back(0);
}
....
Related
I'm creating a graph taking a file as input and I want to calculate the shortest path, to do so I used SPF algorithm. I have a few file I can use to see if it works, and here comes the problem because it works until I try it with the biggest one (which has over 1 million vertex and 2 million edges), considering that the second for dimension has about 700k vertex and 100 million edges and with it it works just fine, what do you think the problem is? I just need some hint I really can't figure it out!
Please be patient with me, I'm new in this comunity and to coding in general, I'm just trying to learn and understand things properly...
It's returning error 3221225725
// Function to compute the SPF algorithm
void shortestPathFaster(int S, int V)
{
// Create array d to store shortest distance
int d[V + 1];
// Boolean array to check if vertex
// is present in queue or not
bool inQueue[V + 1] = { false };
// Initialize the distance from source to
// other vertex as INT_MAX(infinite)
for (int i = 0; i <= V; i++) {
d[i] = INT_MAX;
}
d[S] = 0;
queue<int> q;
q.push(S);
inQueue[S] = true;
while (!q.empty()) {
// Take the front vertex from Queue
int u = q.front();
q.pop();
inQueue[u] = false;
// Relaxing all the adjacent edges of
// vertex taken from the Queue
for (int i = 0; i < graph[u].size(); i++) {
int v = graph[u][i].first;
int weight = graph[u][i].second;
if (d[v] > d[u] + weight) {
d[v] = d[u] + weight;
// Check if vertex v is in Queue or not
// if not then push it into the Queue
if (!inQueue[v]) {
q.push(v);
inQueue[v] = true;
}
}
}
}
// Print the result
print_distance(d, V);
}
The problem is most likely here:
int d[V + 1];
Firstly, variable length arrays are non-standard. Secondly, if V is large you will overflow the stack.
Solution: replace this with std::vector. bool inQueue[V + 1] should be treated similarly.
Also, replace char buffer[BUFFER_SIZE]; with std::string. You'll be glad you did.
I have to find the weight between all edges in a graph, so since the edges are bidirectional I dont want to include 2 -> 1 if I already have 1 -> 2 (since they will have the same weight). The edges are stored in a vector from structure Edge. My initial idea was to look up, if an edge that has the start and end positions swapped and has the same weight already exists, and if this is the case, just dont do anything. However, I dont exactly know how to put it into code, so any help would be appreciated. Also any approaches that could optimise the solution are also welcome.
struct Vertex {
Vertex(const int i = 0) : index {i}, key {max_key}, parent_index {undef}, processed {false} {}
int index; // vertex identifier
int key; // temporary minimal weight (Prim algorithm)
int parent_index; // temporary minimal distance neighboor vertex (Prim algorithm)
int processed; // flag used to mark vertices that are already included in V'
static constexpr int max_key = std::numeric_limits<int>::max();
static const int undef = -1;
};
struct Edge {
Edge(int va, int vb, int w) : vi1 {va}, vi2 {vb}, weight {w} { }
int vi1; //start point
int vi2; //end point
int weight;
};
struct Graph {
int N; // number of vertices
std::vector<Vertex> V; // set of vertices
std::vector<Edge> E; // set of edges
std::vector<Edge> MST; // minimal spanning tree
const int* weights_table; // weights given as distance matrix
};
The problem is here in find I know this is a lot of irrelevant code, but I post it so that you can picture it more clearly. If there is no connection between 2 vertices they have weight of -1
// construct vertices and edges for a given graph
void createGraph(Graph& G) {
// TODO 5.1a: clear V and E and insert all vertex objects and edge objects
// - vertices are numbered (labeled) from 0 to N-1
// - edges exist if and only if there is positive distance between two vertices
// - edges are bidirectional, that is, edges are inserted only once between two vertices
G.E.clear();
G.V.clear();
for(int i = 0; i < G.N; i++){
Vertex V (i);
G.V.push_back(V);
}
for(int i = 0; i < G.N; i++){
for(int j = 0; j < G.N; j++){
Edge Ed (i,j,0);
int weight = getWeight(G,i,j);
if(weight > 0){
Ed.weight = weight;
auto it = find(G.E.begin(), G.E.end(), ....);
if( it != G.E.end() ) continue;
G.E.push_back(Ed);
}
}
}
}
Thanks!
since the edges are bidirectional
You can construct Edges such that v1 <= v2, then there is only one representation of each possible edge.
struct Edge {
Edge(int va, int vb, int w) : vi1 {std::min(va, vb)}, vi2 {std::max(va, vb)}, weight {w} { }
int vi1; // earlier point
int vi2; // later point
int weight;
};
Aside: prefer constructing the Edge in place
for(int i = 0; i < G.N; i++){
for(int j = G.N - 1; j >= 0 + i; j--){
int weight = getWeight(G,i,j);
if(weight > 0){
G.E.emplace_back(i, j, weight);
}
}
}
Okay, I think I got it, by changing the second for loop to look this way, but I am also curious to see how would the syntax would look like if find is being used
for(int i = 0; i < G.N; i++){
for(int j = G.N - 1; j >= 0 + i; j--){
Edge Ed (i, j , 0);
int weight = getWeight(G,i,j);
if(weight > 0){
Ed.weight = weight;
G.E.push_back(Ed);
}
}
}
Source:
here
Problem:
Given n nodes labeled from 0 to n - 1 and a list of undirected edges (each edge is a pair of nodes), write a function to find the number of connected components in an undirected graph.
Approach:
class Solution
{
public:
int countComponents(int n, vector<vector<int>>& edges)
{
std::vector<bool> v(n, false);
int count = 0;
for(int i = 0; i < n; ++i)
{
if(!v[i])
{
dfs(edges, v, i);
count++;
}
}
return count;
}
void dfs(std::vector<std::vector<int>>& edges, std::vector<bool>& v, int i)
{
if(v[i] || i > edges.size())
return;
v[i] = true;
for(int j = 0; j < edges[i].size(); ++j)
dfs(edges, v, edges[i][j]);
}
};
Error:
heap-buffer overflow
I am not understanding why my code is causing a heap-buffer overflow for the test case:
5
[[0,1],[1,2],[2,3],[3,4]]
Any suggestions on how to fix my code would be really appreciated.
My guess is that your edges vector has only four elements in it for the provided input, since there is no outgoing edge from vertex 4. Your dfs function then eventually recurs into the point where i == 4, but your edges vector has only 4 elements, thus the last valid possition is edges[3].
I suggest that you represent a vertex with no outgoing vertices with an empty vector.
Also, the second part of the if statement
if(v[i] || i > edges.size())
return;
seems unecceserry and should probably just be
if(v[i])
return;
I would like to write prims and dijkstra algoritms that create a MST. But I do not know what is the best way to represent graph in c++.
I could represent an edge by pair of two ints for example vector 0 to 1 would be pair(0,1);
typedef pair<int, int> Edge;
And then the prims function would take Vector of pairs that consist of an edge and its weight.
void prims(vector<pair<Edge, int>>);
I think that this way is not the best one, could anyone tell me what way would be the best to represent a graph?
I have been implementing Dijkstra some time ago for finding paths in binary images. I represented a graph as a vector of a struct GraphNodes that contained a vector of Connections that contained all the connections of the node to other nodes. Each connection has its distance attribute, which is the weight of the edge. Here are the two structs I used:
//forward declaration
struct GraphNode;
struct Connection {
Connection() : distance(1) { };
Connection(GraphNode* ptr, double distance) : ptr(ptr), distance(distance) { };
bool operator==(const Connection &other) const;
GraphNode* ptr;
double distance;
};
struct GraphNode {
GraphNode() : connections(8), predecessor(NULL), distance(-1) { };
cv::Point point;
double distance;
GraphNode* predecessor;
std::vector<Connection> connections;
};
bool Connection::operator==(const Connection &other) const {
return ptr == other.ptr && distance == other.distance;
}
The distance attribute of the GraphNode is the distance it currently has in the Dijkstra algorithm, so the distance of the shortest currently known distance to the start node. At the beginning this is initialized with -1.
I then implemented the Dijkstra algorithm like this:
std::vector<cv::Point> findShortestPathDijkstra(std::vector<GraphNode>& graph, int startNodeIndex, int destNodeIndex) const {
GraphDistanceSorter sorter(graph);
std::set<GraphNode*, GraphDistanceSorter> unusedNodes(sorter);
for (int i = 0; i < graph.size(); ++i) {
unusedNodes.insert(&graph[i]);
}
while (unusedNodes.size() > 0) {
GraphNode* currentNode = *unusedNodes.begin();
if (currentNode->distance == -1) {
return std::vector<cv::Point>();
}
if (currentNode == &graph[destNodeIndex]) break;
unusedNodes.erase(currentNode);
//update distances of connected nodes
for (Connection const& con : currentNode->connections) {
/*here we could check if the element is really in unusedNodes (search, O(log n)), but this would
actually take longer than calculating the new distance (O(1)), which will in this case always be greater
than the old one, so the distance is never updated for nodes not in unusedNodes ()*/
double newDistance = currentNode->distance + con.distance;
if (newDistance < con.ptr->distance || con.ptr->distance == -1) {
unusedNodes.erase(con.ptr);
con.ptr->distance = newDistance;
con.ptr->predecessor = currentNode;
unusedNodes.insert(con.ptr);
}
}
}
//now trace back the path as a list of points
std::vector<cv::Point> points;
GraphNode* current = &graph[destNodeIndex];
points.push_back(current->point);
while (current != &graph[startNodeIndex]) {
if (current->predecessor == NULL) return std::vector<cv::Point>();
current = current->predecessor;
points.push_back(current->point);
}
return points;
}
As you see there is a set unusedNodes that contains all the unused nodes so far. It only contains pointers on graphNodes. The actual graph representation is in the vector. The advantage of having a set is, that it is always sorted according to a certain criterion. I implemented my own sorter GraphDistanceSorter that sorts the GraphNodes according to the distance criterion of the Dijkstra algorithm. This way I just have to pick the first node from the set and know that it's the one with the smallest distance:
struct GraphDistanceSorter {
bool operator() (const GraphNode* lhs, const GraphNode* rhs) const;
};
bool GraphDistanceSorter::operator() (const GraphNode* lhs, const GraphNode* rhs) const {
if (lhs->distance == rhs->distance) {
return lhs < rhs;
} else {
if (lhs->distance != -1 && rhs->distance != -1) {
if (lhs->distance != rhs->distance) {
return lhs->distance < rhs->distance;
}
} else if (lhs->distance != -1 && rhs->distance == -1) {
return true;
}
return false;
}
}
The two main ways to represent graphs learned in theoretical computer-science are adjacency matrix and adjacency lists.
Adjacency Matrix is as shown in the photo below is an n*n matrix and a[i][j] represents the edge between node i and node j so if it's a weighted graph it can be an integer instead of a boolean value for unweighted graphs.
adjacency matrix (photo source: google)
On the other hand, adjacency lists is a set of linked-lists (n-set to be exact), the i-th set has exactly the nodes i is connected to.
in this case you will need some additional way to save edge distance for example you can build your own class Edge as following
class Edge
{
int destination, length;
Edge* next = 0;
}
and use it for your linked-list. How ever I am used to std::vector<std::pair<int, int>> a[N] to define a list of pairs and a[i][j].first would be the j-th neighbor of nod i and a[i][j].second the length of the edge between them.
For undirected graph your can add i to j neighbors as well.
So it's also a flexible way to represent graphs.
adjacency lists (image source: google photos)
So now let's talk complexity, I will try to keep it as simple as possible:
We habe n lists, each has the #(edges going out of node i)
so the total number is sum of this numbers which is the total number of edges E.
That means place complexity is O(E) which is at most 5 * n in a sparse-graph in comparison to O(N^2) in adjacency matrix. (We need a linear factor of E to represent it).
Now let's consider visiting all neighbors of a nod x:
in adjacency matrix we will go through the whole x-th line and if it's not 0 there's an edge there which is O(N).
In adjacency lists it's again exactly the number of neighbors of x which can though reach O(N).
But if we are visiting all neighbors of all Nodes (which is the case in Dijkstra when updating dis array), you will need to visit n elements n times in adjacency lists which is also O(N^2) time complexity while in adjacency lists it's exactly the sum of the number of neighbors - again E. which means we need also O(E) to visit all neighbors of all edges.
And sind all edges are given usually in the input O(E) would pass as calculation time, but O(N^2) would be a high complexity for constraints of N <= 10^6 for example.
At the end I will leave you with my usual implementation of diffreent variants of graphs using adjacency lists (vector as a list):
#include<iostream>
#include<vector>
int main(){
const int N = 5;
int n, e;
std::vector<std::pair<int, int>> graph[N], inverse[N];
std::vector<int> unweighted[N], undirectedUnweighted[N];
std::cin >> n >> e;
for(int i = 0; i < e; i++)
{
int x, y, z;//z is length of edge
std::cin >> x >> y >> z;
//substitute 1 from x, y if they starts from 1
graph[x].push_back(std::make_pair(y, z));
inverse[y].push_back(std::make_pair(x, z));
unweighted[x].push_back(y);
undirectedUnweighted[x].push_back(y);
undirectedUnweighted[y].push_back(x);
}
return 0;
}
Simple form for representing graph (finding neighbors and degrees for vertices)
#include<iostream>
/** Representing graphs in c++ programming language */
using namespace std;
int main() {
cout << "\033[1;33mNote: if there are no neighbourhood between vertices write '-' symbol!\033[0m\n"<<endl;
int number_of_vertices;
cout<<"\033[1;32mPlease enter number of vertices: \033[0m";
cin>>number_of_vertices;
int max_num_of_neighbours;
cout<<"\033[1;32mPlease enter maximum number of neighbours: \033[0m";
cin>>max_num_of_neighbours;
char array[number_of_vertices][max_num_of_neighbours];
char vertices[number_of_vertices];
cout<<"\033[1;33mPlease sign vertices with lowercase alphabet letters: \033[0m"<<endl;
for(int i = 0; i < number_of_vertices; i ++) {
cout<<(i+1)<<" = ";
cin>>vertices[i];
}
for(int i = 0; i < number_of_vertices; cout<<endl, i ++) {
cout<<"\033[1;32mPlease enter neighbours for \033[0m"<<vertices[i]<<" --> ";
for(int j = 0; j < max_num_of_neighbours; j ++) {
cin>>array[i][j];
}
}
for(int i = 0; i < number_of_vertices; cout<<endl, i ++) {
cout<<"\033[1;34mNeighbours for \033[0m"<<"\033[1;35m"<<vertices[i]<<"\033[0m"<<" --> ";
int deg = 0;
for(int j = 0; j < max_num_of_neighbours; j ++) {
if(array[i][j] != '-') {
deg ++;
}
if(array[i][j] == '-') {
cout<<"\033[1;31m"<<array[i][j]<<"\033[0m"<<"\t";
} else {
cout<<"\033[1;32m"<<array[i][j]<<"\033[0m"<<"\t";
}
}
cout<<"\033[1;36m"<<"deg["<<"\033[0m"<<"\033[1;35m"<<vertices[i]<<"\033[0m"<<"\033[1;36m"<<"] = "<<"\033[0m"<<deg;
}
cout<<endl<<"\033[1;33mRemember that '\033[1;31m-\033[0m\033[1;33m' shows when two vertices aren't adjacent!\033[0m"<<endl;
}
For adding interactivity I used How do I output coloured text to a Linux terminal? for changing color of text
I'm working on a C++ program where we must traverse a graph of vertices and weighted edges in a way that we start at a user-specified vertex and then end at the same vertex after a certain desired distance has been traveled.
I am not sure how to implement this with code, but I have this so far:
void DijkstrasShortestPath()
{
while (vertices.size() != 0)
{
Vertex* u = extract_min(vertices);
vector<Vertex*>* adjVertex = AdjVertices(u);
const int size = adjVertex->size();
for (int i=0; i<size; ++i)
{
Vertex* v = adjVertex->at(i);
int distance = travel_dist(u, v) +
u->distFromStart;
if (distance < v->distFromStart)
{
v->distFromStart = distance;
v->previous = u;
}
}
delete adjVertex;
}
}
Vertex* extract_min(vector<Vertex*>& vertices)
{
int size = vertices.size();
if (size == 0) {
return NULL;
}
int minimum = 0;
Vertex* min = vertices.at(0);
int i = 0;
for( i=1; i<size; ++i)
{
Vertex* temp = vertices.at(i);
if( temp->distFromStart < min->distFromStart) {
min = temp;
minimum = i;
}
}
vertices.erase(vertices.begin() + minimum);
return min;
}
vector <Vertex*>* AdjVertices(Vertex* vert)
{
vector<Vertex*>* adjVertex = new vector <Vertex*> ();
const int size = edges.size();
for(int i=0; i<size; ++i)
{
Edge* edge = edges.at(i);
Vertex* adjacent = NULL;
if (edge->intersection1 == vert)
{
adjacent = edge->intersection2;
}
else if (edge->intersection2 == vert)
{
adjacent = edge->intersection1;
}
if (adjacent && vertices_check(vertices, adjacent))
{
adjVertex->push_back(adjacent);
}
}
return adjVertex;
}
int travel_dist(Vertex* u, Vertex* v)
{
const int size = edges.size();
for(int i=0; i<size; ++i)
{
Edge* edge = edges.at(i);
if (edge->street_connection(u, v))
{
return edge->distance;
}
}
return -1;
}
bool vertices_check(vector<Vertex*>& vertices, Vertex* vert)
{
const int size = vertices.size();
for(int i=0; i<size; ++i)
{
if (vert == vertices.at(i))
{
return true;
}
}
return false;
}
This is essentially the Dijkstra's Shortest Path algorithm, which is not exactly what I want. What I'm trying to do is get the program to calculate a route thats distance is within 1 unit of a user-specified distance and starts and ends at the same vertex.
Is there any way I can do that by changing what I have?
Does this call for a Breadth-First Search or a Depth-First search instead of Dijkstra's Algorithm?
Dijkstra's algorithm will only store the shortest path from the start node to any other node. What you want instead is to keep track of all paths that lead to a node. If you have those, you can check every time you find a new path to a node if there is a path that has been found before whose length plus the length of the new path is within one unit of the user specified distance. If you then walk one path forward and the other one back you have your loop.
One possible method.
You could use a bounded best-first search.
Create a structure P which stores a list of nodes forming the potential loop, along with the length of the loop created so far.
Seed the search at the specified vertex S by making a separate P-structure for each of the nodes which S links to. Add these P-structures to a priority queue which is sorted by the length of the path described by the P-structure.
Consider each of these nodes in turn removing their P-structures from the priority queue, copying their P-structures, and appending the nodes they link to. If the node to be added is already present in the P-structure and not it is not S, then discard the structure and do not consider that route any longer. Similarly, if any path exceeds the specified cost C, then discard that path and do not consider it any longer. If the path has not been discarded, add its associated P-structure to the priority-queue.
If S does appear in a P-structure twice and the length of the path described by the P-structure is within the permitted range, then exit successfully. If not, continue the search until the priortiy-queue is empty.
You may be able speed up the algorithm by using an admissible heuristic to guesstimate the distance from a given node to S, adding this to the established cost of the path-in-progress, and using the sum as the sorting key for the priority queue. Such heuristics are at the heart of the A* algorithm, which may be of interest to you.
Is this clear? If not, I could writing a short example code.