Function-call operator overload & destructor help - c++

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.

Related

Getting full path in C++ adjacency list Dijkstra

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

Why does this function throw munmap_chunck() in C++?

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);
}

Getting Permutations with Repetitions in this special way

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.

A* Pathfinder in C++

I am trying to write an A* pathfinder to learn C++ and am struggling with a specific bug.
These are the features/assumptions of the program:
The path contains "walls" which cannot be passed
All paths cost the same value (1)
Diagonal movement is allowed
Only one goal
Here is my code:
#include <algorithm>
#include <iostream>
#include <vector>
#include <tuple>
#include <cmath>
#include <set>
using Graph = std::vector<std::vector<int>>;
using Position = std::tuple<int, int>;
class Node;
using Nodes = std::vector<Node>;
constexpr int WALL_BLOCK = 1;
constexpr int USER_BLOCK = 3;
constexpr int GOAL_BLOCK = 2;
class Node {
private:
int row;
int column;
Node* parent = nullptr;
int f = 0;
int g = 0;
int h = 0;
public:
Node() : row(-1), column(-1) {}
Node(int row, int column) : row(row), column(column) {}
Node(int row, int column, Node *parent) : row(row), column(column) {
if (this->parent != nullptr && *this->parent == *this) {
throw "Node cannot be parented to itself";
}
this->parent = parent;
}
~Node() {}
Node* get_parent() const { return this->parent; }
int get_f() const { return this->f; }
int get_g() const { return this->g; }
int get_h() const { return this->h; }
Position get_position() const { return std::make_tuple(row, column); }
void set_f(int f) { this->f = f; }
void set_g(int g) { this->g = g; }
void set_h(int h) { this->h = h; }
bool operator==(Node const &node) const {
return this->row == node.row && this->column == node.column;
}
bool operator<(Node const &node) const {
return this->row < node.row && this->column < node.column;
}
friend std::ostream& operator<<(std::ostream& os, Node const &node);
};
std::ostream& operator<<(std::ostream& os, Node const &node) {
auto row = node.row;
auto column = node.column;
os << "Node(("
<< row << ", "
<< column << "), "
<< node.get_g() << ", "
<< node.get_h() << ", "
;
Node* parent = node.get_parent();
if (parent != nullptr) {
os << parent;
} else {
os << "nullptr";
}
os << ")";
return os;
}
inline bool is_walkable(Node const &node, Graph const &graph) {
int column;
int row;
std::tie(row, column) = node.get_position();
return graph[row][column] != WALL_BLOCK;
}
Position get_first_index(Graph const &graph, int block);
Position get_end(Graph const &graph) {
return get_first_index(graph, GOAL_BLOCK);
}
Position get_first_index(Graph const &graph, int block) {
for (int row = 0, max = graph.size(); row < max; ++row) {
auto line = graph[row];
auto found = std::find(line.begin(), line.end(), block);
if (found != line.end()) {
return std::make_tuple(row, found - line.begin());
}
}
return {-1, -1};
}
inline int get_h(Node const &node, Node const &reference) {
auto node_position = node.get_position();
auto reference_position = reference.get_position();
auto node_position_row = std::get<0>(node_position);
auto node_position_column = std::get<1>(node_position);
auto reference_position_row = std::get<0>(reference_position);
auto reference_position_column = std::get<1>(reference_position);
return (
std::pow((node_position_row - reference_position_row), 2) +
std::pow((node_position_column - reference_position_column), 2)
);
}
Position get_start(Graph const &graph) {
return get_first_index(graph, USER_BLOCK);
}
Nodes get_children(Node &node, Graph const &graph) {
Nodes children;
int row;
int column;
std::tie(row, column) = node.get_position();
for (int row_offset = -1; row_offset < 2; ++row_offset) {
for (int column_offset = -1; column_offset < 2; ++column_offset) {
if (row_offset == 0 and column_offset == 0) {
// (0, 0) will always be `node`. We can't let `node` be a child
// of itself so we have to `continue` here
//
continue;
}
Graph::size_type node_row = row + row_offset;
Graph::size_type node_column = column + column_offset;
if (node_row >= graph.size()) {
continue;
}
if (node_column >= graph[node_row].size()) {
continue;
}
children.push_back({
static_cast<int>(node_row),
static_cast<int>(node_column),
&node
});
}
}
return children;
}
Nodes trace(Node const &node) {
Node* parent = node.get_parent();
Nodes path;
std::set<Node> seen;
while (parent != nullptr) {
auto parent_node = *parent;
if (std::find(seen.begin(), seen.end(), parent_node) != seen.end()) {
// If this happens, `parent` is already in `path`. To avoid
// a cyclic loop from happening, we will break out, instead.
//
break;
}
seen.insert(parent_node);
path.push_back(parent_node);
parent = parent->get_parent();
}
return path;
}
Nodes a_star(Graph const &graph, Node const &user, Node const &goal) {
Nodes open_list {user};
Nodes closed_list;
while (open_list.size() != 0) {
Node current_node = open_list[0];
unsigned int current_index = 0;
for (int index = 0, max = open_list.size(); index < max; ++index) {
auto node = open_list[index];
if (node.get_f() < current_node.get_f()) {
current_node = node;
current_index = index;
}
}
if (current_node == goal) {
auto path = trace(current_node);
std::reverse(path.begin(), path.end());
return path;
}
open_list.erase(open_list.begin() + current_index);
closed_list.push_back(current_node);
auto children = get_children(current_node, graph);
for (auto &child : children) {
if (std::find(closed_list.begin(), closed_list.end(), child) != closed_list.end()) {
continue;
}
if (!is_walkable(child, graph)) {
continue;
}
child.set_g(child.get_parent()->get_g() + 1);
child.set_h(get_h(child, goal));
child.set_f(child.get_g() + child.get_h());
bool add = true;
for (auto const &open : open_list) {
if (child == open && child.get_g() > open.get_g()) {
add = false;
break;
}
}
if (add) {
open_list.push_back(child);
}
}
}
return {};
}
int main() {
Graph graph = {
{0, 0, 0, 0, 1, 0, 0},
{0, 3, 0, 0, 1, 0, 2},
{0, 0, 0, 0, 1, 0, 0},
{0, 0, 0, 0, 1, 0, 0},
{0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 1, 0, 0},
{0, 0, 0, 0, 1, 0, 0},
};
int start_row = 0;
int start_column = 0;
std::tie(start_row, start_column) = get_start(graph);
int end_row = 0;
int end_column = 0;
std::tie(end_row, end_column) = get_end(graph);
auto user = Node(start_row, start_column);
auto goal = Node(end_row, end_column);
std::cout << user << std::endl;
std::cout << goal << std::endl;
auto nodes = a_star(graph, user, goal);
std::cout << "Path: [";
for (auto const &node : nodes) {
std::cout << "("
<< std::get<0>(node.get_position())
<< ", "
<< std::get<1>(node.get_position())
<< "), ";
}
std::cout << "]" << std::endl;
return 0;
}
When I run this on my computer, I get this output:
Node((1, 1), 0, 0, nullptr)
Node((1, 6), 0, 0, nullptr)
Path: [(1, 6), ]
Path: should have returned a full list starting from (1, 1) to the goal, (1, 6) But instead of [(1, 1), (1, 2), (2, 3), (3, 3), (4, 4), (3, 5), (2, 6), (1, 6), ]. we got [(1, 6), ]. I think the reason for this is because current_node contains itself as a parent. So trace breaks early to avoid a cyclic loop. And that's why there's only one in the output. It's weird that any Node would have itself as a parent because the constructor throws an exception if that happens.
In fact on my machine, every parent of every single node is the same address, 0x7fffffff9140, which shouldn't be happening.
My only guess to this is that current_node is being initialized in the while loop so maybe even though the Node position / f / g / h are changing, the pointer in memory never does. So all the nodes end up getting the same parent that way.
That may or may not be the issue but either way, I'm not sure how to fix it. Any advice would be helpful. Thank you!
This constructor body is totally messed up, you are reading from member this->parent which is always initialized to nullptr (by the brace-or-equal-initializer).
Node(int row, int column, Node *parent) : row(row), column(column)
{
if (this->parent != nullptr && *this->parent == *this) {
throw "Node cannot be parented to itself";
}
this->parent = parent;
}
Second major problem, this is passing a reference to an object current_node whose lifetime is insufficient:
auto children = get_children(current_node, graph);
By the time you read any of the "children" out of the list, the node pointed to by their parent member has already been destroyed.
The copy in closed_list would survive, but that can't be used to store a parent pointer either, because of the iterator invalidation rules for std::vector. If you change closed_list to std::list<Node> then the element addresses would be stable.
But then when a_star returns all the objects pointed to by instance parent pointers will die anyway. trace(current_node) results in a collection of nodes whose parent members point outside the collection.
Your best bet is probably to preallocate enough Node objects for the whole graph instance, use an index instead of a pointer as parent, and update the parent member instead of setting it during construction. Index values, unlike pointers, remain meaningful no matter how many times a data structure is copied.
(Also, string literals make bad exception objects)

I am getting garbage in erase function

I am getting garbage in my user defined vector.Garbage comes after erase function this is the code
#if 1 // Make it 1 to enable this code
#include <iostream>
using namespace std;
class Vector{
protected:
int l;
int* v;
public:
Vector():l(0),v(0){
cout << "\nBase class: Vector: Default constructor" << endl;
}
Vector( int len ):l(len),v(new int[l]) {
cout << "Vector: Overloaded constructor" << endl;
}
void set( int i, int val)
{
if( l )
v[i] = val;
else{
cout << "Error: zero size vector\n exiting..." <<endl;
exit(5);
}
}
int get( int i)
{
if( l )
return v[i];
else{
cout << "Error: zero size vector\n exiting..." <<endl;
exit(5);
}
}
~Vector(){
cout << "Base dextructor" <<endl;
delete [] v;
}
};
class Vector1:public Vector{
private:
public:
Vector1():Vector(){
cout << "Derived class: Vector1:: Default constructor" << endl;
}
//my functions
int size()
{
return l;
}
int front()
{
return Vector::get(0);
}
int end()
{
return Vector::get(l-1);
}
void swap(int a,int b)
{
int temp=v[a];
v[a]=v[b];
v[b]=temp;
}
void find(int val)
{
bool flag=false;
for(int i=0;i<l;i++)
{
if(v[i]==val)
{
flag=true;
}
}
if(flag==true)
cout<<"\nValue ="<<val<<" =Exists in Vector";
else
cout<<"\nValue ="<<val<<" =doesnot Exists in Vector";
}
void erase(int val)
{
int *temp=new int[l-1];
int k=0;
for(int i=0;i<l;i++)
{
if(v[i]!=val)
{
temp[i]=v[i];
k++;
}
}
delete []v;
l=l-1;
v=new int[l];
for(int i=0;i<l;i++)
{
v[i]=temp[i];
}
}
int resize( int len )
{
if( l )
delete [] v;
l = len;
v = new int[l];
return (v!=0);
}
void set( int i, int val)
{
if( i>= 0 && i<l )
Vector::set( i, val );
else{
cout << "Error: index out of range\n exiting..." <<endl;
exit(3);
}
}
int get( int i)
{
if( i>= 0 && i<l )
return Vector::get(i);
else{
cout << "Error: index out of range\n exiting..." <<endl;
exit(3);
}
}
int & operator [](int i)
{
return v[i];
}
Vector1( int len ):Vector(len)
{
}
};
int main()
{
Vector vec;
Vector1 v1;
v1.resize( 4 );
v1.set( 2, 4);
v1.set( 1, 5);
v1.set( 0, 6);
int x = v1[2];
v1[3] = 77;
cout<<"\nSize of vector is=\n"<<v1.size();
//v1.set( 5, 4); // erroneous value
cout<<"\nFront of vector is=\n"<<v1.front();
cout<<"\nEnd of vector is=\n"<<v1.end();
//do swap between values
cout<<"\n";
v1.swap(1,3);
//now values are
cout<<"v1[1] is equals to= "<<v1[1];
cout<<"v1[3] is equals to= "<<v1[3];
v1.find(5);
v1.find(100);
cout<<"\nNow v1[0] is equals to= \n"<<v1[0];
v1.erase(6);
cout<<"\nNow v1[0] is equals to= \n"<<v1[0]<<" "<<v1[1];
cout<<"\n";
}
#endif
Output is this
v1[0] should be be 5
In your erase function, you are not actually erasing the given number, you just copy every number in the vector that is not equal to the given number to a new array. E.g., if your internal array in the vector contained the numbers 2, 4, 6 and 8, and you are erasing 6, you just copy 2, 4 and 8 into a new array into indices 0, 1 and 3, while keeping slot 2 in the new array as undefined. Then you make a third array that is one shorter than the new array, and copy everything except the last item into the third array, including the undefined slot 2, but not the last slot (which should have been kept).
I think that using temp[k] = v[i] would solve your problem in the erase function, but note that it still won't be a perfect solution because the item being deleted might occur multiple times in the vector, or it might not be there at all, so it is not always the case that the new length is one smaller than the old length.
In the erase function, you should have temp[k] = v[i] and not temp[i] = v[i].