I have a decision tree that includes node and answer that leads us to another nodes. Answers begin with ":" and nodes are the rest.
I have to do a function that delete a subtree from a specific node. For example If I want to delete node "brand?", I want that after that the tree will print from car-color? to blue-is-beautiful
I don't success doing this deletion in the right way because I think I have to delete also the answer red and don't know how to do that.
class Answer
{
public:
string ans;
Node* son;
Answer(string s, Node* p) { ans = s; son = p; }
};
class Node
{
public:
Node(string v) { isLeaf = true; value = v; }
list<Answer*> answersList;
string value;
bool isLeaf;
};
void Tree::del(Node* t)
{
if (t->isLeaf)
return;
for (list<Answer*>::iterator it = t->answersList.begin(); it != t->answersList.end(); it++)
{
del((*it)->son);
delete((*it));
*it = NULL;
}
if (t)
{
delete t;
t = NULL;
}
}
Now having understood the problems (highly restrictive requirements and what is causing your code to fail), I now have an answer for you.
The issue is, that you need to remove the node you've deleted from the collection it is stored in.
For this purpose, you need to use an alternate version of your search to detect, which child has the value you are looking for.
Due to the requirement of 'not adding any additional functions', there are two ways to go about this.
One is to employ recursion using an anonymous function, the other is 'check the child prior to diving into it'.
The following code fragment uses a DIY-Lambda-Functor, which employs the recursion method.
void Tree::deletefromNode(string val)
{
bool didFindValue = false;
std::function<bool (Node *, const string &)> functor;
class Functor
{
public:
Functor(Tree *owner, bool &didFindValue) : owner(owner), didFindValue(didFindValue)
{
}
bool deleteFromNode(Node *node, const string &value)
{
bool foundMatch = false;
if (node)
{
foundMatch = (node->value == value);
if (!foundMatch)
{
for (list<Answer*>::iterator it = node->answersList.begin(); it != node->answersList.end();)
{
Node *childNode = (*it)->son;
if (deleteFromNode(childNode, value))
{
owner->del(childNode);
it = node->answersList.erase(it);
didFindValue = true;
}
else
it++;
}
}
}
return foundMatch;
}
private:
Tree *owner;
bool &didFindValue;
};
Functor(this, didFindValue).deleteFromNode(root, val);
if (didFindValue)
cout << "Value not found" << endl;
}
Related
I want to solve a problem regarding numbers saved in a tree structure.
I would like to create one class called Tree and another one called Element (in this case would be an Integer but it could be whatever) and make it the most adequate way based on C++ standards. It should be possible to add childs to a specific element in the tree and traceback the parent of each element.
#include <iostream>
#include <vector>
class Element
{
public:
Element() = delete;
explicit Element(int value, Element* parent = nullptr):
value_(value), parent_(parent), children_() {}
int getValue() const { return value_; }
Element* getParent() { return parent_; }
// it will throw if idx out of bounds
Element* getChild(size_t idx) { return children_.at(idx).get(); }
size_t numChildren() const { return children_.size(); }
Element* insertChild(int value)
{
std::cout << "new elem: " << value << std::endl;
children_.emplace_back(std::make_unique<Element>(value, this));
return children_.back().get();
}
bool moveChildren2Parent()
{
if (isRoot()) return false;
for (auto& c : children_)
{
// children change parent
c->parent_ = parent_;
parent_->children_.emplace_back(std::move(c));
}
children_.clear();
return true;
}
bool removeChild(size_t idx)
{
if (children_.size() <= idx) return false;
children_.erase(children_.begin()+idx);
return true;
}
bool isRoot() { return parent_ == nullptr; }
private:
int value_;
Element* parent_;
std::vector<std::unique_ptr<Element> > children_;
};
void checkChilds(Element* element) {
for (int i = 0; i < element->numChildren(); i++)
{
if (element->getChild(i)->numChildren() == 1)
{
element->getChild(i)->moveChildren2Parent();
element->removeChild(i);
i--;
} else if (element->getChild(i)->numChildren() > 1)
{
checkChilds(element->getChild(i));
}
}
}
int main()
{
auto root = std::make_shared<Element>(0);
Element* _ = root->insertChild(1)->insertChild(3)->insertChild(5);
Element* last_child = root->insertChild(2)->insertChild(4)->insertChild(7);
last_child->getParent()->insertChild(6);
for (int i=0;i<root->numChildren();i++)
{
if (root->getChild(i)->numChildren()==1)
{
root->getChild(i)->moveChildren2Parent();
root->removeChild(i);
i--;
}
else if (root->getChild(i)->numChildren()>1)
{
checkChilds(root->getChild(i));
}
}
return 0;
}
My objective is to create a tree and after if each element just has one child remove that element while keeping the leaves.
My code works but I would like to know improvements to make it better looking based on C++ standards.
EDIT
Thanks to the answer #pptaszni and after adapting it to my specific problem at hand this is the result. I think my implementation to go over all the elements checking if the number of childs they have is equal 1 and if so remove is not nicely written. Do you know how could I optimize it (the last for loop in main and function checkChilds)?
is there any specific reason you need two different class for element and tree?
i suggest, have just one class which have one data member which will store a node value and two pointers to point at two different child objects.
following is the suggestion based on what i have have understood from your question.
class Node
{
int value;
Node* left_child = nullptr;
Node* right_child = nullptr;
//methods for modifying tree.
};
To answer your first question: don't use operator[], because it inserts default-constructed element if it doesn't exist in the map. You can use at instead.
Then about your architecture: it doesn't look very good, because having a child, it's not obvious how to get parent, having a parent, it's not obvious how to get it's children, your std::map key is supposed to be unique and you also use it as a value for your Elements. I suggest to apply at least the following:
class Element
{
public:
Element() = delete;
explicit Element(int value, Element* parent = nullptr):
value_(value), parent_(parent), left_child_(nullptr), right_child_(nullptr) {}
int getValue() const { return value_; }
Element* getParent() { return parent_; }
Element* getLeftChild() { return left_child_.get(); }
Element* getRightChild() { return right_child_.get(); }
Element* insertLeftChild(int value)
{
if (left_child_)
{
std::cout << "already exists" << std::endl;
return left_child_.get(); // already exists
}
std::cout << "new left elem: " << value << std::endl;
left_child_ = std::make_unique<Element>(value, this);
return left_child_.get();
}
bool isRoot() { return parent_ == nullptr; }
private:
int value_;
Element* parent_;
std::unique_ptr<Element> left_child_;
std::unique_ptr<Element> right_child_;
};
int main()
{
auto root = std::make_shared<Element>(1);
Element* last_child = root->insertLeftChild(2)->insertLeftChild(3)->insertLeftChild(4);
std::cout << last_child->getValue() << std::endl;
std::cout << last_child->getParent()->getValue() << std::endl;
std::cout << last_child->getParent()->getParent()->getValue() << std::endl;
std::cout << last_child->getParent()->getParent()->getParent()->getValue() << std::endl;
std::cout << last_child->getParent()->getParent()->getParent()->isRoot() << std::endl;
return 0;
}
Now you have the access to parent and children from every element and you can start building your tree. Then there are further concerns like Element comparison operator (if needed), only 2 children per node or maybe more, pointers invalidation on every tree modification etc. It is a big topic in general.
======= EDIT =========
To answer OP's concern about multiple children, and to provide an example of removing the node while preserving the leaves (children):
class Element
{
public:
Element() = delete;
explicit Element(int value, Element* parent = nullptr):
value_(value), parent_(parent), children_() {}
int getValue() const { return value_; }
Element* getParent() { return parent_; }
// it will throw if idx out of bounds
Element* getChild(size_t idx) { return children_.at(idx).get(); }
size_t numChildren() const { return children_.size(); }
Element* insertChild(int value)
{
std::cout << "new elem: " << value << std::endl;
children_.emplace_back(std::make_unique<Element>(value, this));
return children_.back().get();
}
bool moveChildren2Parent()
{
if (isRoot()) return false;
for (auto& c : children_)
{
// children change parent
c->parent_ = parent_;
parent_->children_.emplace_back(std::move(c));
}
children_.clear();
return true;
}
bool removeChild(size_t idx)
{
if (children_.size() <= idx) return false;
children_.erase(children_.begin()+idx);
return true;
}
bool isRoot() { return parent_ == nullptr; }
private:
int value_;
Element* parent_;
std::vector<std::unique_ptr<Element> > children_;
};
int main()
{
auto root = std::make_shared<Element>(1);
Element* last_child = root->insertChild(2)->insertChild(3)->insertChild(4);
last_child->getParent()->insertChild(5);
std::cout << "numChildren: " << last_child->getParent()->numChildren() << std::endl;
last_child->getParent()->moveChildren2Parent();
std::cout << "numChildren: " << last_child->getParent()->numChildren() << std::endl;
last_child->getParent()->removeChild(0); // element with value 3 removed, it's children already transferred
std::cout << last_child->getValue() << std::endl;
std::cout << last_child->getParent()->getValue() << std::endl;
std::cout << last_child->getParent()->getParent()->getValue() << std::endl;
std::cout << last_child->getParent()->getParent()->isRoot() << std::endl;
return 0;
}
It is just one of many possibilities, particular implementation choice always depends on the system requirements.
You have several problems with your code there. The most glaring one I can see is that you are freely converting between int pairs and elements. Classes provide a way to strongly type our code, if int, int is mean to represent an Element, make it an Element!
The reason you have to declare a default constructor is because you have already declared a constructor. The compiler will only generate a default one for you if you havnt already declared one.
To make the compiler explcitly generate one for you use default. Like this:
Element() = default;
To fix your weak typing issues you should use Element for m_parent and when adding an item to the Tree, if you need to do stuff like setting values of the parent then do that to the item you are passed to add. Like this
...
private:
Element m_parent;
...
void addElement(const Element& parent, int value)
{
Element elem = Element(value, parent);
m_elements.insert(std::pair<int,Element>(value,elem));
}
...
You also probably want to use a different container than std::map. If you want to have a concept of 'elements' then I would use std::set, store the element value or key as a member and overload the < operator (to allow sorting) to return the operation on the values/keys. std::set would allow you to search the tree (using a lambda with find_if) for keys or values as well as anything else (like parent matching).
This use of storing the parent as another Element also enables you to iterate back up the tree if you wanted too (independent of your container).
I have written a compiler that works in its current form. However I feel like I am overusing dynamic casting when differentiating different sub classes for the nodes while visiting nodes on the AST. Here is an example:
I have a recursive method called visit that dynamically casts a node parameter for each node sub class. It will check if the cast was successful and then take appropriate action as such:
VariableValue visit(Node *node) {
Num* num = dynamic_cast<Num*>(node);
if (num != NULL) {
return visit_Num(num);
}
delete num;
BinOp* binop = dynamic_cast<BinOp*>(node);
if (binop != NULL) {
return visit_BinOp(binop);
}
delete binop;
UnOp* unop = dynamic_cast<UnOp*>(node);
if (unop != NULL) {
return visit_UnaryOp(unop);
}
delete unop;
...
The issue is I have a lot of different node sub classes, so these blocks of if statements go on for a while. I feel this approach is not very efficient on CPU and memory as a cast will be created for each node sub class and will have to be checked against each one. I have attempted to improve this by using delete when a cast fails.
Is there a better way to achieve my desired results within a polymorphic environment?
For some context here are the node classes including the parent:
class Node {
public:
virtual std::string toString() = 0;
virtual int getNodeAttribute() { return 0; }
Token token;
Node(){}
};
class BinOp : public Node{
public:
Node *left, *right;
Token op;
BinOp(Node *cleft, Token cop, Node *cright) {
left = cleft;
Node::token = cop;
op = cop;
right = cright;
}
std::string toString() {
return "BinOp Node";
}
};
class Num : public Node{
public:
TokenValue value;
Num(Token ctoken) {
Node::token = ctoken;
value = ctoken.value;
}
std::string toString() {
return "Num Node";
}
};
class UnOp : public Node {
public:
Node *expr;
UnOp(Token ctoken, Node *cexpr) {
Node::token = ctoken;
expr = cexpr;
}
std::string toString() {
return "UnOp Node";
}
};
...
And here is how a node is generated within the parser (Num node for example)
Node* Parser::factor() {
...
else if (token.type == TOKENTYPE::INTEGER) {
eat(TOKENTYPE::INTEGER);
Num* numNode = new Num(token);
return numNode;
}
...
Im writing a program that simulates a vacuum cleaning a room. There is an initial state of dirty spots and I want to use various AI algorithms to find the best paths to clean the room. By making the algorithms separate from the actual problem I think my solution will be very modular.
Each algorithm only knows about States. Each state can be expanded to children states. Here is my first algorithm, UniformCost:
#include<iostream>
#include<set>
class State {
public:
State(){}
bool operator< (const State& s) const;
bool isGoal();
std::set<State> expand();
};
class UniformCost {
private:
State startState;
std::set<State> closedList; //list of no repeated states
public:
State start;
void setStart(State s);
State* getSolution();
};
void UniformCost::setStart(State st) {
start = st;
}
State* UniformCost::getSolution() {
closedList.insert(start);
while(!closedList.empty()) {
State cur = *closedList.begin();
if(cur.isGoal()) {
return &cur;
}
closedList.erase(cur);
std::set<State> children = cur.expand();
for (std::set<State>::iterator i = children.begin(); i != children.end(); ++i) {
closedList.insert(*i);
}
}
}
My main application creates the initial Node that is a child class of State.
class Node : public State {
public:
std::pair<int,int> loc;
int g_val;
std::set<std::pair<int,int> > dirt;
std::vector<char> path;
bool isGoal() {
return dirt.size() == 0;
}
bool operator< (const State& s) const {
Node n = (Node) s;
if(loc == n.loc) {
return false;
}
if(g_val <= n.g_val) {
return true;
}
return false;
}
std::set<State> expand() {
std::set<State> ret;
return ret;
}
};
How can I override the operator in the Node class that is expecting a "operator< (const State&)"? Or a more general question, how would I handle future "casting" of States?
I am trying to make a generic Segment Tree Class for updates and range queries.
Instead of assuming that the elements would just be integers and the operation to be done over a range of elements would be their sum or product, i would want the user to provide the type T of the element and a function, which i named compose.
This function takes in two parameters of type T and returns a value of the same type T. This return value is the result when that desired operation is performed over range of 2 elements which i can use to perform that same operation on a range of any number of elements.
The class is as follows:
#include <functional>
template<class T>
class SegmentTree {
public:
class binary_function_unitype: public std::binary_function<T,T,T> {
public:
virtual T operator() (T arg1, T arg2) {};
};
private:
class Node {
public:
T value;
int seg_start, seg_end;
Node* left;
Node* right;
Node (T value, int seg_start, int seg_end, Node* left=0, Node* right=0) {
this->value = value;
this->seg_start = seg_start;
this->seg_end = seg_end;
this->left = left;
this->right = right;
}
};
// Not expecting the compose function to be robust enough.
T composeUtil (T arg1, T arg2) {
if (arg1!=0 && arg2!=0)
return compose(arg1,arg2);
else if (arg1!=0)
return arg1;
else if (arg2!=0)
return arg2;
}
// Creating the Segment Tree.
Node* createTree (T leaves[], int start, int end) {
// base case - leaf of tree.
if (start==end)
return new Node(leaves[start],start,start,0,0);
// general case.
int mid = start + (end-start)/2;
Node* left = createTree(leaves,start,mid);
Node* right = createTree(leaves,mid+1,end);
T retValue = composeUtil(left->value,right->value);
return new Node(retValue,start,end,left,right);
}
// Range Query helper.
T queryUtil (Node* root, int start, int end) {
int seg_start = root->seg_start, seg_end = root->seg_end;
if (seg_start>end || seg_end<start)
return 0;
else if (seg_start>=start && seg_end<=end)
return root->value;
else
return compose( queryUtil(root->left,start,end), queryUtil(root->right,start,end));
}
// Helper function for Updating the Segment Tree.
void updateUtil (Node* root, int position, T updatedValue) {
int seg_start = root->seg_start, seg_end = root->seg_end;
if(seg_start>position || seg_end<position)
return;
else if(seg_start==seg_end)
root->value = updatedValue;
else
root->value = composeUtil(root->left->value,root->right->value);
}
// Freeing the memory allocated to the Segment Tree.
void destroyTree(Node* root) {
if (root->left!=0)
destroyTree(root->left);
if (root->right!=0)
destroyTree(root->right);
delete root;
}
Node* root;
binary_function_unitype compose;
public:
SegmentTree (T leaves[], binary_function_unitype compose, int start, int end) {
this->compose = compose;
this->root = createTree(leaves, start, end);
}
T query (int start, int end) {
return queryUtil(root, start, end);
}
void update (int position, T updatedValue) {
updateUtil(root, position, updatedValue);
}
~SegmentTree () {
destroyTree(root);
}
};
When I tried to use this class, it turns out that the compose function, which I took in as a paramater is not being used, on the contrary the one from the class binary_function_unitype is being used.
I expected that the function definition from the user would override the one in class binary_function_unitype and my work would be done. But that did not happen. The program using this class is as follows:
#include <iostream>
#include "SegmentTree.h"
using namespace std;
class Compose: public SegmentTree<int>::binary_function_unitype {
public:
int operator() (int arg1, int arg2) {
return arg1+arg2;
}
};
int main()
{
int num;
cin>>num;
int arr[num];
for(int i=0;i<num;i++)
cin>>arr[i];
Compose compose;
SegmentTree<int> segTree(arr, compose, 0, num-1);
int s,e;
cin>>s>>e;
cout<<segTree.query(s-1,e-1);
return 0;
}
Can somebody tell me whats the flaw in my approach or if I misunderstood some basic concept about using inheritance or templates in C++ ?
Thanks.
The constructor takes a binary_function_unitype by value, so it will slice.
So I have a project of which I want to switch to Astar due to speed reasons.
But C++ is not my strongest point. Could anyone help me (or tell me how to do the..) converting the algorythm from Dijkstra to Astar?
I found this Astar implementation:
http://code.google.com/p/a-star-algorithm-implementation/
But I don't know how to use it with my existing code.
Here is the graph file which got the algorithm:
#include "Graph.h"
#include <iostream>
#include <algorithm>
#include <stack>
Graph::Graph(void)
{
}
Graph::~Graph(void)
{
while(!mNodes.empty())
{
delete mNodes.back();
mNodes.pop_back();
}
}
void Graph::addNode(int name, bool exists, Node** NodeID )
{
Node* pStart = NULL;
mNodes.push_back(new Node(name,exists));
std::vector<Node*>::iterator itr;
itr = mNodes.begin()+mNodes.size()-1;
pStart = (*itr);
if(exists == true)pStart->DoesExist_yes();
*NodeID = pStart;
}
void Graph::connect_oneway(Node* pFirst, Node* pSecond, int moveCost)
{
if(pFirst != NULL && pSecond != NULL)
{
pFirst->createEdge(pSecond, moveCost);
}
}
#define MAX_NODES (32768)
#define MAX_CONNECTIONS (5)
#include <time.h>
int * Graph::findPath_r(Node* pStart, Node* pEnd)
{
int *arr = new int[MAX_NODES+2];
for (int i=0; i<MAX_NODES; i++)
arr[i] = -1;
arr[0] = 0;
if(pStart == pEnd)
{
return arr;
}
std::vector<Node*> openList;
openList.push_back(pStart);
Node* pCurrNode = NULL;
while(!openList.empty())
{
//Get best node from open list (lowest F value).
//Since we sort the list at the end of the previous loop we know
//the front node is the best
pCurrNode = openList.front();
//Exit if we're are the goal
if(pCurrNode == pEnd)
break;
//Remove the node from the open list and place it in the closed
openList.erase(openList.begin());
pCurrNode->setClosed(true); //We use a flag instead of a list for speed
//Test all of the edge nodes from the current node
std::vector<Edge*>* pEdges = pCurrNode->getEdges();
Node* pEdgeNode = NULL;
for(std::vector<Edge*>::iterator i = pEdges->begin(); i != pEdges->end(); ++i)
{
pEdgeNode = (*i)->pNode;
//If it's closed we've already analysed it
if(!pEdgeNode->getClosed() && pCurrNode->DoesExist() == true)
{
if(!inList(pEdgeNode,&openList))
{
openList.push_back(pEdgeNode);
pEdgeNode->setGCost(pCurrNode->getGCost()+(*i)->moveCost);
pEdgeNode->calcFCost();
pEdgeNode->setParent(pCurrNode);
}
else
{
//If this is a better node (lower G cost)
if(pEdgeNode->getGCost() > pCurrNode->getGCost()+(*i)->moveCost)
{
pEdgeNode->setGCost(pCurrNode->getGCost()+(*i)->moveCost);
pEdgeNode->calcFCost();
pEdgeNode->setParent(pCurrNode);
}
}
}
}
//Place the lowest F cost item in the open list at the top, so we can
//access it easily next iteration
std::sort(openList.begin(), openList.end(), Graph::compareNodes);
}
//Make sure we actually found a path
if(pEnd->getParent() != NULL)
{
//Output the path
//Use a stack because it is LIFO
std::stack<Node*> path;
while(pCurrNode != NULL)
{
path.push(pCurrNode);
pCurrNode = pCurrNode->getParent();
}
int counter = 0;
arr[1] = 0;
while(!path.empty())
{
arr[counter+2] = path.top()->getName();
counter++;
arr[1] += path.top()->getGCost();
path.pop();
}
arr[0] = counter;
return arr;
}
return arr;
}
bool Graph::inList(Node* pNode, std::vector<Node*>* pList)
{
for(std::vector<Node*>::iterator i = pList->begin(); i != pList->end(); ++i)
{
if((*i) == pNode)
{
return true;
}
}
return false;
}
bool Graph::compareNodes(Node* pFirst, Node* pSecond)
{
return pFirst->getFCost() < pSecond->getFCost();
}
void Graph::reset(void)
{
for(std::vector<Node*>::iterator i = mNodes.begin(); i != mNodes.end(); ++i)
{
(*i)->reset();
}
}
The function for finding the path is this one:
Graph::findPath_r
What I really want to do is preserve the edges (because they decide if the road is both or one-way).
Here are the other files:
Graph.h
#ifndef _GRAPH_H_
#define _GRAPH_H
#include "Node.h"
class Graph
{
public:
Graph(void);
~Graph(void);
//void addNode(int name, bool exists);
void addNode(int name, bool exists, Node** NodeID );
void connect_oneway(int ppFirst, int ppSecond, int moveCost);
void connect_oneway(Node* pFirst, Node* pSecond, int moveCost);
//int * findPath_r(int start, int end);
int * findPath_r(Node* pStart, Node* pEnd);
void reset(void);
private:
void findNodesx(int firstName, Node** ppFirstNode);
bool inList(Node* pNode, std::vector<Node*>* pList);
static bool compareNodes(Node* pFirst, Node* pSecond);
std::vector<Node*> mNodes;
};
#endif
Node.h
#ifndef _NODE_H_
#define _NODE_H_
#include <string>
#include <vector>
//Forward declare Node so Edge can see it
class Node;
struct Edge
{
Edge(Node* node, int cost) : pNode(node), moveCost(cost){}
Node* pNode;
int moveCost;
};
class Node
{
public:
Node(void);
Node(int name, bool exists);
~Node(void);
void createEdge(Node* pTarget, int moveCost);
void setGCost(int cost);
void setClosed(bool closed);
void setParent(Node* pParent);
int getGCost(void);
int getFCost(void);
bool getClosed(void);
Node* getParent(void);
int getName(void);
bool DoesExist(void);
bool DoesExist_yes(void);
std::vector<Edge*>* getEdges(void);
void calcFCost(void);
void reset(void);
private:
int mGCost;
int mTotal;
bool mClosed;
Node* mpParent;
int mName;
bool mHeur;
std::vector<Edge*> mEdges;
};
#endif
Node.cpp
#include "Node.h"
Node::Node(void)
{
}
Node::Node(/*const std::string&*/int name, bool exists) : mGCost(0), mTotal(0), mClosed(false), mpParent(NULL), mName(name), mHeur(exists)
{
}
Node::~Node(void)
{
while(!mEdges.empty())
{
delete mEdges.back();
mEdges.pop_back();
}
}
int Node::getName(void)
{
return mName;
}
void Node::createEdge(Node* pTarget, int moveCost)
{
mEdges.push_back(new Edge(pTarget, moveCost));
}
void Node::setClosed(bool closed)
{
mClosed = closed;
}
bool Node::getClosed(void)
{
return mClosed;
}
std::vector<Edge*>* Node::getEdges(void)
{
return &mEdges;
}
int Node::getGCost(void)
{
return mGCost;
}
void Node::setGCost(int cost)
{
mGCost = cost;
}
void Node::calcFCost(void)
{
mTotal = mGCost;
}
void Node::setParent(Node* pParent)
{
mpParent = pParent;
}
int Node::getFCost(void)
{
return mTotal;
}
bool Node::DoesExist(void)
{
return mHeur;
}
bool Node::DoesExist_yes(void)
{
mHeur = true;
return true;
}
Node* Node::getParent(void)
{
return mpParent;
}
void Node::reset(void)
{
mGCost = 0;
mTotal = 0;
mClosed = false;
mpParent = NULL;
}
You mentioned a library on GoogleCode. It is node clear what you want to do with, and I think the best is to write your implementation yourself.
First, you should know that Dijsktra is a special case of A*. In A*, you have an heuristic, named h; A* = possible implementation of Dijsktra when h is the null function.
Then, about your implementation, let's start with Node. It will need the following functions:
constructor, destructor
create/get edge
set/get parent
set/is closed (for speed)
set/get GCost
set/get FCost
set/is obstacle (name way more descriptive than 'DoesExist')
set/get position
reset
// optional method:
get name
Hopefully, this part of your code won't change a lot. The heuristic code will be placed in the pathfinder. The Edge class is left untouched.
Now the big one: Graph. You won't need to delete any of your public methods.
You will need a heuristic method. For the implementation which will be described, you will need an admissible consistent heuristic:
it must not over-estimate the distance to the goal (admissible)
it must be monotone (consistent)
The general case signature is int getHCost(Node* node);. If you always return 0, you will have a Dijsktra algorithm, which is not what you want. Here we will take the euclidiean distance between the node and the goal. Slower to compute than manhattan distance, but better results. You can change this afterwards.
int getHCost(Node* node, Note* goal);
This implies you must place your nodes in the 3d space. Note that the heuristic is a heuristic, ie, an estimation of the distance.
I won't write the code. I will write some pseudo-code adapted to your situation. The original pseudocode is located on the Wikipedia A* page. This pseudo-code is your findPath_r function:
function A*(start,goal)
set all nodes to not closed // The set of nodes already evaluated.
openset = {start} // The set of tentative nodes to be evaluated, initially containing the start node
start.gcost = 0 // Cost from start along best known path.
// Estimated total cost from start to goal through y.
start.fcost = start.gcost + getHCost(start, goal)
while openset is not empty
current = the node in openset having the lowest f_cost (usually the first if you use a sorted list)
if current == goal
return construct_path(goal)
remove current from openset
current.closed = true
for each neighbor in (node connected by edge in current.edges) // Here is the condition for one-way edges
if neighbor.closed or neighbor.obstacle
continue
gcost = current.gcost + dist_between(current,neighbor) // via edge distance
if neighbor not in openset
add neighbor to openset
neighbor.parent = current
neighbor.gcost = gcost
neighbor.fcost = neighbor.gcost + getHCost(neighbor, goal)
else if gcost < neighbor.gcost
neighbor.parent = current
neighbor.gcost = gcost
neighbor.fcost = neighbor.gcost + getHCost(neighbor, goal)
update neighbor position in openset
return failure
function construct_path(current_node)
std::vector<Node*> path
while current_node != 0
path.push_front(current_node)
current_node = current_node.parent
return path
The implementation above use one-way edges.
You were able to write Dijsktra algorithm in C++, so writing this pseudocode in C++ shouldn't be a problem.
Second part, performances. First, measure ;).
I have some hints that can improve performances:
use a memory pool for allocation deallocation
use an intrusive list for the open list (you can also make it auto-sorted with this technique)
I advise you to read A* for beginners, which is a useful reading, even if you don't use tilemap.