I want to get full path in adjacency list Dijkstra algorithm using C++ queue. Graph edges are oriented.
Dijkstra algorithm works fine and I understand why. However getting full path is a bit more complicated to me, this usually described much less than Dijkstra algorithm itself. I tried to reused a few solutions (this, for example) I've found for square matrix, but it didn't worked for my adjacency list implementation.
Part I'm stucked with:
int dijkstra(int start, int finish)
{
//some code
parent.resize(vertex_count(), -1);
while (!q.empty()) {
//some code
for (auto edge : link[current]) {
if (dist[current] + edge.weight < dist[edge.to]) {
dist[edge.to] = dist[current] + edge.weight;
parent[edge.to] = start;
q.push(QueueVertex(edge.to,dist[edge.to]));
}
}
}
path(parent);
return dist[finish];
}
void path(vector<int> parent) {
for (auto i = 0; i < parent.size(); i++) {
if (parent[i] != -1)
cout << i << ' ';
}
cout << endl;
}
Full code:
#include <iostream>
#include <queue>
#include <algorithm>
#include <vector>
#include <climits>
#define INF INT_MAX
using namespace std;
struct Edge
{
int to;
int weight;
Edge() {}
Edge(int to, int weight) : to(to), weight(weight) {}
void read() {
cin >> to >> weight;
}
};
struct QueueVertex
{
int number;
int dist;
QueueVertex(int number, int dist) : number(number), dist(dist) {}
};
bool operator<(const QueueVertex& v1, const QueueVertex& v2) {
return v1.dist > v2.dist;
}
class Graph
{
vector<vector<Edge>> link;
vector <int> dist;
vector<int> parent = {};
public:
Graph(int vertex_count) :
link(vertex_count) {}
void add_edge_u(int from, int to, int weight) { //unoriented
link[from].push_back(Edge(to, weight));
link[to].push_back(Edge(from, weight));
}
void add_edge_o(int from, int to, int weight) { //oriented
link[from].push_back(Edge(to, weight));
}
int vertex_count() const {
return link.size();
}
int dijkstra(int start, int finish)
{
dist.resize(vertex_count(), INF);
dist[start] = 0;
parent.resize(vertex_count(), -1);
priority_queue <QueueVertex> q;
q.push(QueueVertex(start, 0));
while (!q.empty()) {
int current = q.top().number;
int current_dist = q.top().dist;
q.pop();
if (current_dist > dist[current]) {
continue;
}
for (auto edge : link[current]) {
if (dist[current] + edge.weight < dist[edge.to]) {
dist[edge.to] = dist[current] + edge.weight;
parent[edge.to] = start;
q.push(QueueVertex(edge.to,dist[edge.to]));
}
}
}
path(parent);
return dist[finish];
}
void path(vector<int> parent) {
for (auto i = 0; i < parent.size(); i++) {
if (parent[i] != -1)
cout << i << ' ';
}
cout << endl;
}
};
int main()
{
{
int n = 3, m = 3, start = 1, finish = 0;
Graph gr(n);
gr.add_edge_o(0, 1, 1);
gr.add_edge_o(1, 2, 2);
gr.add_edge_o(2, 3, 5);
gr.add_edge_o(3, 0, 4);
int dist = gr.dijkstra(start, finish);
cout << dist << endl;
return 0;
}
Desirable output (program getting 11 just fine, but not 1 2 3 0 part):
1 2 3 0
11
Thank you.
Your path function makes no sense. You should be using the parent array to walk backwards from the goal state to the start. As written, this function simply outputs all the parents. Consider something like this:
deque<int> path;
while(finish != -1)
{
path.push_front(finish);
finish = (finish == start) ? -1 : parent[finish];
}
for (int node : path) cout << (node + 1) << ' ';
cout << endl;
Note that the above uses a std::deque for convenience, since the path is built in reverse. But you can use a std::vector if you wish, and either reverse it or walk over it with a reverse_iterator.
Now that the path is being built correctly, you'll quickly see another problem, which is that your parent table is not being built correctly. You're doing this:
parent[edge.to] = start; //<-- incorrect
That looks like a copy/paste error, because you don't want every node's parent to point back at the start. The parent of the edge being examined is stored in current:
parent[edge.to] = current; //<-- correct
Related
I wrote a function, which traverses a graph to find an euler tour. I have a vector tour, that stores the final tour. If I initialize it with the starting vertex 0, then I get a munmap_chuck(): invalid pointer error after calling depth first search the first time. If I don't initialze it, the error is not thrown. But the dfs_tour() function doesn't change anything at tour, and it is also not changed anywhere else until then. The initialization is necessary to make the function work in the end.
So why does this happen?
The graph is a multigraph represented with a two dimensional map vertex1 --> vertex2 --> number of edges between them. It is unweighted.
#include <bits/stdc++.h>
using namespace std;
typedef map<int, map<int, int>> map_2d;
void dfs_tour(int start, map_2d &graph, vector<int> &subtour);
void remove_edge(map_2d &graph, int v1, int v2);
vector<int> euler_tour(map_2d &graph);
int main() {
map_2d graph;
graph[0][1] = 1;
graph[0][3] = 2;
graph[0][5] = 1;
graph[1][2] = 1;
graph[1][6] = 2;
graph[2][3] = 1;
graph[3][4] = 1;
graph[4][5] = 1;
euler_tour(graph);
}
vector<int> euler_tour(map_2d &graph) {
// If this is not initialized, the error is not thrown.
vector<int> tour{ 0 };
vector<int> subtour;
int start = 0;
while (!graph.empty()) {
// The error is thrown here after the function return in the first iteration.
cout << "before dfs\n";
dfs_tour(start, graph, subtour);
cout << "after dfs\n";
if (graph[start].empty()) {
graph.erase(start);
}
for (int i = 0; i < tour.size(); i++) {
if (tour[i] == start) {
tour.insert(tour.begin() + i, subtour.begin(), subtour.end());
break;
}
}
for (auto it = graph.begin(); it != graph.end(); it++) {
if (!it->second.empty()) {
start = it->first; break;
}
}
subtour.clear();
}
return tour;
}
// Auxiliary Functions
void remove_edge(map_2d &graph, int v1, int v2) {
graph.at(v1).at(v2) -= 1;
graph.at(v2).at(v1) -= 1;
if (graph.at(v1).at(v2) == 0) {
graph.at(v1).erase(v2);
graph.at(v2).erase(v1);
}
if (graph.at(v1).empty()) {
graph.erase(v1);
}
if (graph.at(v2).empty()) {
graph.erase(v2);
}
}
void dfs_tour(int start, map_2d &graph, vector<int> &subtour) {
if (!graph[start].empty()){
int next = graph[start].begin()->first;
cout << start << " to " << next << endl;
remove_edge(graph, start, next);
dfs_tour(next, graph, subtour);
}
subtour.push_back(start);
}
I have implemented the Weighted graph along with BFS and DFS. But I cannot figure a way out how to stop the traversal when a destination node (specified by user) is reached. Like user should enter the src and dest, and the BFS and DFS algorithm should print the tree until that specified node is reached. I have tried some things but I just cannot understand how to do this. I am attaching the code, any help would be appreciated.
#include "iostream"
#include "vector"
#include "queue"
#include "stack"
using namespace std;
typedef pair<int , int> Pair;
struct Edge{
int src, dest, weight;
};
class Graph{
public:
vector<vector<Pair>> adjacencyList;
Graph(vector<Edge> const &edges, int N)
{
adjacencyList.resize(N);
for(auto &edge: edges)
{
int src = edge.src;
int dest = edge.dest;
int weight = edge.weight;
adjacencyList[src].push_back(make_pair(dest,weight));
adjacencyList[dest].push_back(make_pair(src,weight));
}
}
};
void BFS(Graph const &graph, int src, vector<bool> &discovered)
{
queue<int> q;
discovered[src] = true;
q.push(src);
while(!q.empty())
{
src = q.front();
q.pop();
cout<<src<<" ";
for(int i = 0; i != graph.adjacencyList[src].size() ;i++)
{
if(!discovered[i])
{
discovered[i] = true;
q.push(i);
}
}
}
}
void DFS(Graph const &graph, int src, vector<bool> &discovered)
{
stack<int> stack;
stack.push(src);
while(!stack.empty()){
src = stack.top();
stack.pop();
if(discovered[src])
{
continue;
}
discovered[src] = true;
cout<<src<< " ";
for(int i = 0 ; i < graph.adjacencyList[src].size() ; i++)
{
if(!discovered[i])
{
stack.push(i);
}
}
}
}
void printGraph(Graph const &graph, int N)
{
for (int i = 0; i < N; ++i) {
for(Pair v: graph.adjacencyList[i])
{
cout<<"("<<i<<" , "<<v.first<<" , "<<v.second<<")";
}
cout<<endl;
}
}
int main()
{
vector<Edge> edges =
{
// `(x, y, w)` —> edge from `x` to `y` having weight `w`
{0,1}, {0,2}, {0,3},
{1, 2}, {2,4}, {3,3}, {4,4}
};
int N = 5;
Graph graph(edges,N);
// printGraph(graph,N);
vector<bool> discovered(N, false);
for(int i = 0; i<N; ++i)
{
if(!discovered[i])
{
BFS(graph, i, discovered);
}
}
cout<<endl;
vector<bool> discovered2(N, false);
for(int i = 0; i<N; i++)
{
if(!discovered2[i])
{
DFS(graph, i , discovered2);
}
}
cout<<endl;
printGraph(graph, N);
}
A recursive design makes this much simpler. here is the depth first version
// set stopNode global
......
bool cPathFinder::depthRecurse(int v)
{
// remember this node has been visted
visted[v] = true;
// is this the sop npde
if ( v == stopNode ) {
return true;
}
// look for new adjacent nodes
for (int w : myGraph.all_neighbors(v)) {
if (!visited[w])
{
// search from new node
if( depthRecurse(w) )
return true;
}
}
}
I have a list of {a,b} and i need all possible combinatations where say n=3.
so:
[a,b,a],
[b,a,b]
[a,a,a]
[b,b,b]
etc.
Is there a name of such a problem
My current solution just uses random sampling and is very inefficient:
void set_generator(const vector<int>& vec, int n){
map<string, vector<int>> imap;
int rcount = 0;
while(1){
string ms = "";
vector<int> mset;
for(int i=0; i<n; i++){
int sampled_int = vec[rand() % vec.size()];
ms += std::to_string(sampled_int);
mset.emplace_back(sampled_int);
}
if(rcount > 100)
break;
if(imap.count(ms)){
rcount += 1;
//cout << "*" << endl;
continue;
}
rcount = 0;
imap[ms] = mset;
cout << ms << endl;
}
}
set_generator({1,2},3);
Let us call b the size of the input vector.
The problem consists in generating all numbers from 0 to b^n - 1, in base b.
A simple solution increments the elements of an array one by one, each from 0 to b-1.
This is performed by the function increment in the code hereafter.
Output:
111
211
121
221
112
212
122
222
The code:
#include <iostream>
#include <vector>
#include <string>
#include <map>
void set_generator_op (const std::vector<int>& vec, int n){
std::map<std::string, std::vector<int>> imap;
int rcount = 0;
while(1){
std::string ms = "";
std::vector<int> mset;
for(int i=0; i<n; i++){
int sampled_int = vec[rand() % vec.size()];
ms += std::to_string(sampled_int);
mset.emplace_back(sampled_int);
}
if(rcount > 100)
break;
if(imap.count(ms)){
rcount += 1;
//cout << "*" << endl;
continue;
}
rcount = 0;
imap[ms] = mset;
std::cout << ms << "\n";
}
}
// incrementation of a array of int, in base "base"
// return false if max is already attained
bool increment (std::vector<int>& cpt, int base) {
int n = cpt.size();
for (int i = 0; i < n; ++i) {
cpt[i]++;
if (cpt[i] != base) {
return true;
}
cpt[i] = 0;
}
return false;
}
void set_generator_new (const std::vector<int>& vec, int n){
int base = vec.size();
std::vector<int> cpt (n, 0);
while (true) {
std::string permut = "";
for (auto &k: cpt) {
permut += std::to_string (vec[k]);
}
std::cout << permut << "\n";
if (!increment(cpt, base)) return;
}
}
int main() {
set_generator_op ({1,2},3);
std::cout << "\n";
set_generator_new ({1,2},3);
}
Following advices of Jarod42, I have
suppressed the useless conversion to a string
used a more elegant do ... while instead of the while true
inversed the iterators for printing the result
Moreover, I have created a templated version of the program.
New output:
111
112
121
122
211
212
221
222
aaa
aab
aba
abb
baa
bab
bba
bbb
And the new code:
#include <iostream>
#include <vector>
#include <string>
#include <map>
// incrementation of a array of int, in base "base"
// return false if max is already attained
bool increment (std::vector<int>& cpt, int base) {
int n = cpt.size();
for (int i = 0; i < n; ++i) {
cpt[i]++;
if (cpt[i] != base) {
return true;
}
cpt[i] = 0;
}
return false;
}
template <typename T>
void set_generator_new (const std::vector<T>& vec, int n){
int base = vec.size();
std::vector<int> cpt (n, 0);
do {
for (auto it = cpt.rbegin(); it != cpt.rend(); ++it) {
std::cout << vec[*it];
}
std::cout << "\n";
} while (increment(cpt, base));
}
int main() {
set_generator_new<int> ({1,2}, 3);
std::cout << "\n";
set_generator_new<char> ({'a','b'}, 3);
}
Besides the concrete answer for integer usage, I want to provide a generic way I needed during test case construction for scenarios with a wide spread of various parameter variations. Maybe it's helpful to you too, at least for similar scenarios.
#include <vector>
#include <memory>
class SingleParameterToVaryBase
{
public:
virtual bool varyNext() = 0;
virtual void reset() = 0;
};
template <typename _DataType, typename _ParamVariationContType>
class SingleParameterToVary : public SingleParameterToVaryBase
{
public:
SingleParameterToVary(
_DataType& param,
const _ParamVariationContType& valuesToVary) :
mParameter(param)
, mVariations(valuesToVary)
{
if (mVariations.empty())
throw std::logic_error("Empty variation container for parameter");
reset();
}
// Step to next parameter value, return false if end of value vector is reached
virtual bool varyNext() override
{
++mCurrentIt;
const bool finished = mCurrentIt == mVariations.cend();
if (finished)
{
return false;
}
else
{
mParameter = *mCurrentIt;
return true;
}
}
virtual void reset() override
{
mCurrentIt = mVariations.cbegin();
mParameter = *mCurrentIt;
}
private:
typedef typename _ParamVariationContType::const_iterator ConstIteratorType;
// Iterator to the actual values this parameter can yield
ConstIteratorType mCurrentIt;
_ParamVariationContType mVariations;
// Reference to the parameter itself
_DataType& mParameter;
};
class GenericParameterVariator
{
public:
GenericParameterVariator() : mFinished(false)
{
reset();
}
template <typename _ParameterType, typename _ParameterVariationsType>
void registerParameterToVary(
_ParameterType& param,
const _ParameterVariationsType& paramVariations)
{
mParametersToVary.push_back(
std::make_unique<SingleParameterToVary<_ParameterType, _ParameterVariationsType>>(
param, paramVariations));
}
const bool isFinished() const { return mFinished; }
void reset()
{
mFinished = false;
mNumTotalCombinationsVisited = 0;
for (const auto& upParameter : mParametersToVary)
upParameter->reset();
}
// Step into next state if possible
bool createNextParameterPermutation()
{
if (mFinished || mParametersToVary.empty())
return false;
auto itPToVary = mParametersToVary.begin();
while (itPToVary != mParametersToVary.end())
{
const auto& upParameter = *itPToVary;
// If we are the very first configuration at all, do not vary.
const bool variedSomething = mNumTotalCombinationsVisited == 0 ? true : upParameter->varyNext();
++mNumTotalCombinationsVisited;
if (!variedSomething)
{
// If we were not able to vary the last parameter in our list, we are finished.
if (std::next(itPToVary) == mParametersToVary.end())
{
mFinished = true;
return false;
}
++itPToVary;
continue;
}
else
{
if (itPToVary != mParametersToVary.begin())
{
// Reset all parameters before this one
auto itBackwd = itPToVary;
do
{
--itBackwd;
(*itBackwd)->reset();
} while (itBackwd != mParametersToVary.begin());
}
return true;
}
}
return true;
}
private:
// Linearized parameter set
std::vector<std::unique_ptr<SingleParameterToVaryBase>> mParametersToVary;
bool mFinished;
size_t mNumTotalCombinationsVisited;
};
Possible usage:
GenericParameterVariator paramVariator;
size_t param1;
int param2;
char param3;
paramVariator.registerParameterToVary(param1, std::vector<size_t>{ 1, 2 });
paramVariator.registerParameterToVary(param2, std::vector<int>{ -1, -2 });
paramVariator.registerParameterToVary(param3, std::vector<char>{ 'a', 'b' });
std::vector<std::tuple<size_t, int, char>> visitedCombinations;
while (paramVariator.createNextParameterPermutation())
visitedCombinations.push_back(std::make_tuple(param1, param2, param3));
Generates:
(1, -1, 'a')
(2, -1, 'a')
(1, -2, 'a')
(2, -2, 'a')
(1, -1, 'b')
(2, -1, 'b')
(1, -2, 'b')
(2, -2, 'b')
For sure, this can be further optimized/specialized. For instance you can simply add a hashing scheme and/or an avoid functor if you want to avoid effective repetitions. Also, since the parameters are held as references, one might consider to protect the generator from possible error-prone usage via deleting copy/assignement constructors and operators.
Time complexity is within the theoretical permutation complexity range.
I have a working implementation of Djikstra's algorithm which calculates the length of the shortest path between any two nodes. But if I need to find the actual path, how can I print that? Thanks!
void djikstra( graph * mygraph )
{
int dist[100] = {INT_MAX};
int i;
//int parent[mygraph->vertices] = {-99};
for ( i = 0; i < 100; i++ )
dist[i] = INT_MAX;
bool arr[100];
for ( i = 0; i < 100; i++ )
arr[i] = false;
int foo;
cout<<"Enter the source vertex\n";
cin>>foo;
dist[foo] = 0;
vector<int> bar;
while (bar.size() != mygraph->vertices)
{
int node = findmin(dist,mygraph->vertices,arr);
arr[node] = true; // so that again and again same node having minimum distance is not returned
bar.push_back(node);
auto it = mygraph->edges[node].begin();
while (it != mygraph->edges[node].end())
{
relax(node,it->first,it->second,dist); // here, it->first represents the node and it->second represents the weight
it++;
}
}
cout<<"Vertex\t"<<"Distance from source\n";
for ( i = 0; i < mygraph->vertices; i++ )
{
cout<<i<<"\t"<<dist[i]<<"\n";
}
cout<<"\n";
return;
}
void relax ( int node, int a, int w, int dist[] )
{
if (dist[a] > dist[node] + w)
{
dist[a] = dist[node] + w;
}
}
You need to also keep a map, which maps from a node, to its "parent".
In this map, the key is a node, and the value is the node used to reach this map.
Obviously the source is going to be a root in this map.
This is done by adding:
parentMap[a] = node;
In the relaxation step:
void relax ( int node, int a, int w, int dist[] )
{
if (dist[a] > dist[node] + w)
{
dist[a] = dist[node] + w;
parentMap[a] = node;
}
}
Once you have this map, getting the path is farily easy, and is done by:
int current = target;
while (current != source) {
cout << current << ' ';
current = parentMap[current];
}
cout << current << ' ';
Note, the above prints the path in reverse order. You can use a list (and add elements to its front rather than printing the elements) to get the path in the correct order.
You can trace back your path through parent node.
#include<bits/stdc++.h>
using namespace std;
class solution{
public:
void printPath(vector<int> parent, int source, int dest)
{
stack<int> st;
int final = dest;
while(parent[dest] != source)
{
dest = parent[dest];
st.push(dest);
}
cout<<source<<"->";
while(!st.empty())
{
cout<<st.top()<<"->";
st.pop();
}
cout<<final<<endl;
}
void shortestPath(int V, vector<pair<int, int>> G[], int source, int dest)
{
vector<int> dist(V, INT_MAX);
dist[source] = 0;
queue<int> q;
q.push(source);
vector<int> parent(V, -1);
parent[source] = source;
while(!q.empty())
{
int u = q.front();
q.pop();
for(auto node: G[u])
{
int v = node.first;
int wt = node.second;
if(dist[v] > dist[u]+wt)
{
dist[v] = dist[u]+wt;
q.push(v);
parent[v] = u;
}
}
}
cout<<"Cost to reach destination: "<<dist[dest]<<"\n";
printPath(parent, source, dest);
}
};
int main(){
int V, E, source, dest;
cout<<"Enter # of Vertex : ";
cin>>V;
cout<<"Enter # of Edges : ";
cin>>E;
int u, v, wt;
cout<<"Add Edges ->\n";
vector<pair<int, int>> adj[V];
while(E--)
{
cin>>u>>v>>wt;
adj[u].push_back(make_pair(v, wt));
}
cout<<"Enter Source : ";
cin>>source;
cout<<"Enter Destination :";
cin>>dest;
solution* obj = new solution;
obj->shortestPath(V, adj, source, dest);
return 0;
}
The output of the above code looks like this,
Enter # of Vertex : 6
Enter # of Edges : 8
Add Edges ->
0 1 2
1 2 5
2 3 5
3 4 5
4 1 50
4 5 600
1 5 1000
2 4 1
Enter Source : 0
Enter Destination :5
Cost to reach destination: 608
0->1->2->4->5
Shortest Path-Printing using Dijkstra's Algorithm for Graph (Here it is implemented for undirected Graph. The following code prints the shortest distance from the source_node to all the other nodes in the graph.
It also prints the shortest path from the source node to the node requested by the user.
Suppose,you need to find the shortest path from A to B in the graph. Then input A as the source node and B as the destination node.
Code
#include<bits/stdc++.h>
using namespace std;
#define INF (unsigned)!((int)0)
const int MAX=2e4;
vector<pair<int,int>> graph[MAX];
bool visit[MAX];
int dist[MAX];
multiset<pair<int,int>> s;
int parent[MAX]; // used to print the path
int main(){
memset(visit,false,sizeof(visit));
memset(dist,INF,sizeof(dist));
memset(parent,-1,sizeof(parent));
int nodes,edges; cin>>nodes>>edges;
for(auto i=0;i<edges;++i){
int a,b,w;
cin>>a>>b>>w;
graph[a].push_back(make_pair(b,w));
graph[b].push_back(make_pair(a,w)); //Comment it to make the Directed Graph
}
int source_node; cin>>source_node;
dist[source_node]=0;
s.insert(make_pair(0,source_node));
while(!s.empty()){
pair<int,int> elem=*s.begin();
s.erase(s.begin());
int node=elem.second;
if(visit[node])continue;
visit[node]=true;
for(auto i=0;i<graph[node].size();++i){
int dest=graph[node][i].first;
int w=graph[node][i].second;
if(dist[node]+w<dist[dest]){
dist[dest]=dist[node]+w;
parent[dest]=node;
s.insert(make_pair(dist[dest],dest));
}
}
}
cout<<"NODE"<<" "<<"DISTANCE"<<endl;
for(auto i=1;i<=nodes;++i){
cout<<i<<" "<<dist[i]<<endl;
}
/*----PRINT SHORTEST PATH FROM THE SOURCE NODE TO THE NODE REQUESTED-------*/
int node_for_path; cin>>node_for_path;
int dest_node=node_for_path;
stack<int> path;
while(parent[node_for_path]!=source_node){
path.push(node_for_path);
node_for_path=parent[node_for_path];
}
path.push(node_for_path);
path.push(source_node);
cout<<"Shortest Path from "<<source_node<<"to "<<dest_node<<":"<<endl;
while(!path.empty()){
if(path.size()==1) cout<<path.top();
else cout<<path.top()<<"->";
path.pop();
}
return 0;
}
/*TEST CASE*/
9 14 //---NODES,EDGES---
1 2 4 //---START,END,WEIGHT---FOR THE NO OF EDGES
2 3 8
3 4 7
4 5 9
5 6 10
6 7 2
7 8 1
8 1 8
2 8 11
8 9 7
9 7 6
9 3 2
6 3 4
4 6 14
1 //---SOURCE_NODE
5 //-----NODE TO WHICH PATH IS REQUIRED
---END---*/
hope it helps
based on #Dipesh Kurasau above, I have done some code below:
Ouput is: total distance =400. Path: 0 5.
If you comment out v0.adjacents.emplace(&v5, 400); you get the same result as #Dipesh Kurasau.
#include <iostream>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <queue>
#include <stack>
using namespace std;
class Vertex
{
public:
Vertex(int i) : id(i) {}
int id;
int toSrcVertex = INT_MAX; // initial distance from this vertex to the source vertex
Vertex* prev = NULL; // point to the previous vertex leading to the source vertex making the shortest path
unordered_map<Vertex*, int> adjacents; //adjacent vertex and its distance;
};
void printPath(Vertex* destination)
{
cout << "total distance =" << destination->toSrcVertex << endl;
cout << "Path:" << endl;
stack<Vertex*> reversed;
while (destination)
{
reversed.push(destination);
destination = destination->prev;
}
while (!reversed.empty())
{
Vertex* t = reversed.top();
reversed.pop();
cout << t->id << " ";
}
cout << "\ndone" << std::flush << endl;
}
void shortest(Vertex* src, Vertex* dest)
{
src->toSrcVertex = 0;
unordered_set<Vertex*> q;
q.insert(src);
unordered_set<Vertex*> visited;
while (!q.empty())
{
Vertex* u = *(q.begin());
q.erase(q.begin());
if (q.empty() && u == dest)
{
// target reached, no more search
return;
}
// vistit all adjacents of Vertex u
for(auto& adj : u->adjacents)
{
Vertex* adjVertex = adj.first;
if (visited.find(adjVertex) != visited.end()) continue;
int distance = adj.second;
int distanceFromUtoAdjVertex = distance + u->toSrcVertex;
if (distanceFromUtoAdjVertex < adjVertex->toSrcVertex)
{
adjVertex->toSrcVertex = distanceFromUtoAdjVertex;
adjVertex->prev = u;
q.insert(adjVertex);
}
}
visited.insert(u);
}
printPath(dest);
}
int main()
{
// construct vertices
Vertex v0(0);
Vertex v1(1);
Vertex v2(2);
Vertex v3(3);
Vertex v4(4);
Vertex v5(5);
// build adjacents (neighboured vertex and distance)
v0.adjacents.emplace(&v1, 2);
v0.adjacents.emplace(&v5, 400);
v1.adjacents.emplace(&v0, 2);
v1.adjacents.emplace(&v2, 5);
v1.adjacents.emplace(&v4, 50);
v1.adjacents.emplace(&v5, 1000);
v2.adjacents.emplace(&v1, 5);
v2.adjacents.emplace(&v3, 5);
v2.adjacents.emplace(&v4, 1);
v3.adjacents.emplace(&v2, 5);
v3.adjacents.emplace(&v4, 5);
v4.adjacents.emplace(&v2, 1);
v4.adjacents.emplace(&v3, 5);
v4.adjacents.emplace(&v1, 50);
v4.adjacents.emplace(&v5, 600);
v5.adjacents.emplace(&v1, 1000);
v5.adjacents.emplace(&v4, 600);
shortest(&v0, &v5);
std::cout << "Hello World!\n" << flush << endl;;
}
I think my destructor is good now... but still not sure how to call print_set from within the operator() overload.
It outputs as it should, but I feel like there is an easy way to just call print_set from within the function call overload... can't seem to get things how I want them..
Header file:
/**
* disjoint_sets class declaration
*
*/
#include<ostream>
#include<string>
#include<list>
class disjoint_sets
{
public:
disjoint_sets(int n); //n is the maximum int that can be in a set, so arrays need to be of size n+1
~disjoint_sets();
bool make_set(int n);
int find_set(int u);
bool union_set(int u, int v);
void print_set(std::ostream& out, int u); //e.g. { 1, 2, 4, 8, 10, }
operator std::string(); //e.g. "{ { 1, 2, 4, 8, 10, }, { 3, 6, }, { 5, }, { 7, }, { 9, }, }"
private:
typedef std::list<int>* listptr;
int* p; //parent array
int* rank; //rank array
listptr* set; //array of pointers. Each set[i] is a pointer is to a list<int> containing the integers in the i'th set
int size; //the size i.e. maximum int that can be in a set
};
Implementation:
/**
* disjoint_sets class implementation
*
*/
#include <iostream>
#include <istream>
#include <sstream>
#include <string>
#include <list>
#include "disjoint.hpp"
/*********************
* CONSTRUCTOR
*********************/
disjoint_sets::disjoint_sets(int n)
{
size = n;
// initialize all arrays to same size (n+1)
p = new int[n+1];
rank = new int[n+1];
set = new listptr[n+1];
// loop through set (array of pointers) and initialize
// each element to an empty list
for (int i(1); i <= n; i++)
{
set[i] = new std::list<int>;
}
}
/*********************
* DESTRUCTOR
*********************/
disjoint_sets::~disjoint_sets()
{
delete[] p;
delete[] rank;
// delete each element in the set first, then delete the set.
for(int i(1); i<size+1; i++)
{
set[i]->clear();
}
delete[] set;
}
/*********************
* MAKE_SET
*********************/
bool disjoint_sets::make_set(int n)
{
// if parent already exists
if (p[n] != 0)
{
return false;
}
// else we need to make a set
else
{
// make itself the parent
p[n] = n;
// use push_front to add the int to front of set
set[n]->push_front(n);
return true;
}
}
/*********************
* FIND_SET
***********************/
int disjoint_sets::find_set(int u)
{
while (u != p[u])
{
u = p[u];
}
// returns parent
return u;
}
/*********************
* UNION_SET
*********************/
bool disjoint_sets::union_set(int u, int v)
{
// assign parents to u, v
u = find_set(u);
v = find_set(v);
// return false if parent is 0, list is empty
if (u == 0 or v == 0)
{
return false;
}
// if u's height is less than v's (not in same set)
if (rank[u] < rank[v])
{
// point u's parent to v (merge them)
p[u] = v;
// splice u out
(*set[v]).splice((*set[v]).end(), (*set[u]));
return true;
}
// u's height is greater than or equal to v's height
else
{
// set v's parent to u
p[v] = u;
// splice v out
(*set[u]).splice((*set[u]).end(), (*set[v]));
return true;
}
// if ranks are equal
if (rank[u] == rank[v])
{
// increment rank of u
rank[u]++;
return true;
}
}
/*********************
* PRINT_SET
*********************/
void disjoint_sets::print_set(std::ostream& out, int u)
{
// begin brace for set
out << "{ ";
// loop through with iter, seperate with comma
for (std::list<int>::iterator iter((*set[u]).begin()); iter != (*set[u]).end(); iter++)
{
out << *iter << ", ";
}
// end brace for set
out << "}";
}
/*********************
* STRING CONVERSION
*********************/
disjoint_sets::operator std::string()
{
// sstream variable
std::stringstream text;
// pointer to int array
int *test;
test = new int[size+1];
// opening paren
text << "{ ";
// start main loop through the array
for (int i(1); i <= (size + 1); i++)
{
// if the root is empty
if (test[find_set(i)] == 0)
{
// set to anything != 0?
test[find_set(i)] = 10;
// check if list is empty
if (set[i]->empty())
{
text << "";
}
else
{
// individual set opening parenthesis
text << "{ ";
// use iterator to loop through and load up the stringstream, separate w/ comma
for (std::list<int>::iterator iter((*set[i]).begin()); iter != (*set[i]).end(); iter++)
{
text << *iter << ", ";
}
// individual set closing parenthesis w/ proper punctuation
text << "}, ";
}
}
}
// closing parenthesis
text << "}";
delete[] test;
return text.str();
}
Driver:
#include<iostream>
#include "disjoint.hpp"
int main()
{
using namespace std;
disjoint_sets ds(12);
for( int i=1; i <= 10; ++i)
ds.make_set(i);
ds.union_set(1,2);
ds.union_set(2,4);
ds.union_set(1,8);
ds.union_set(3,6);
ds.union_set(2,10);
//{ { 1, 2, 4, 8, 10, }, { 3, 6, }, { 5, }, { 7, }, { 9, }, }
cout << string(ds) << endl;
}
Just focusing on the destructor:
// delete[] set; // need special conditions for this?
Yes, you need to delete each element in the set first and then delete the set. Use std::shared_ptr if you are not interested to do the memory management.
As a hint: You might get better answers if you ask concrete questions.