Tree Traversals with smart pointers - c++

I have implemented a simple binary tree class in C++.
have using smart pointers objects to hold the pointers to each node (shared for children and weak for parent).
I was trying to implement a nested class for custom iterator (in-order, pre-order and post-order), but I couldn't figure out how to implement efficiently the .next() method.
how can I get current position in traversal without holding the entire tree in a priority queue.
the tree nodes are a struct -
struct node : std::enable_shared_from_this
{
T _val;
std::weak_ptr<node> _parent;
std::shared_ptr<node> _leftChild;
std::shared_ptr<node> _rightChild;
// CONSTRUCTORS
node(T val): _val(val){}
node(T val, weak_ptr<node> parent): _val(val), _parent(parent){}
};

You need to know which child each node is, and from that you can derive the next node from the structure. You can do that with pointer equality
struct iter_base {
std::shared_ptr<node> current;
bool isRoot() const { return !current->_parent.lock(); }
bool isLeft() const { auto parent = current->_parent.lock(); return parent && (current == parent->_leftChild); }
bool isRight() const { auto parent = current->_parent.lock(); return parent && (current == parent->_rightChild); }
};
E.g. for inorder:
If you have a right child, follow that node's left descendants until there are no more, the last one is the next node.
Otherwise, if you are a left child, the next node is your parent.
Otherwise, if you are a right child, follow parent pointers until you find one that is a left child, and the next node is the parent of that left child. You've reached the end if you get to the root doing this.

Related

Doubts on classes when implementing binary search trees

I am studying generic binary search trees (BST) and AVL trees (AVL) on some notes that contain implementation pseudocodes. I am a bit puzzled about some details of their implementation.
The BST is based on the struct Node below
struct Node{
int key;
Node* parent;
Node* left;
Node* right;
//constructors
}
//methods
The AVL version is basically the same with a few fields more for balancing the tree (I'll call it AVLNode for clarity, but there's no such distinction on the notes):
struct AVLNode{
int key;
int height;
int size;
AVLNode* parent;
AVLNode* leftchild;
AVLNode* rightchild;
//constructors
}
//methods
A lot of operations are the same between the two trees and I can easily use templates in order to reuse them on both trees. However, consider the operation insert, which inserts a new node. The code for a BST is something like
//Insert node with key k in tree with root R
void insert(const int& k, Node* root){
Node* N=find(k, root); //finds where to insert the node
if (N->key>k)
N->leftchild=new Node(k,N); //inserts as a left child
else
N->rightchild=new Node(k,N); //inserts as a right child
}
Now, the point is that the insert operation of an AVL tree is basically the same. The pseudocode presented in the notes is as follows:
void avlInsert(int k, AVLNode* R){
insert(k,R); //same operations as for Nodes, shown above
AVLNode* N=find(x,R); //find node inserted (generic operation for BST)
rebalance(N); //perform balancing operations specific to AVL trees
}
I'm a bit puzzled at this point, I know that the above is just a pseudocode but I was wondering whether there is a way to reuse the operation insert already provided for Node. Using template specialization would just mean writing a different specialization insert<AVLNode> for AVLNode, so that's not what I'm referring to.
I think a way would be to define AVLNode as a child class of Node and then use something like
struct AVLNode : Node {
//implementation
}
void avlInsert(int k, AVLNode* R){
Node *root=R;
insert(k,root);
AVLNode* N=find(x,R);
rebalance(N);
}
but I'm not quite sure this would work and I don't know how to manage the pointers to parent and the childs (i.e. they must be pointers to Node inside Node and to AVLNode inside AVLNode).
Is there a way to avoid rewriting the same code?
You could use CRTP here. This would allow you to create the left right and parent nodes in the baseclass. For example consider something like this:
template<typename T>
struct BaseNode{
int key;
T* parent;
T* left;
T* right;
};
struct AVLNode : public BaseNode<AVLNode>{
int height;
int size;
AVLNode(const int&k, AVLNode*root){};
AVLNode(){};
};
struct Node : public BaseNode<Node>{
Node(const int&k, Node*root){};
Node(){};
};
template<typename T>
T* find(const int& k, T* root){return root;};
template<typename T>
void insert(const int& k, T* root){
T* N=find(k, root); //finds where to insert the node
if (N->key>k)
N->left=new T(k,N); //inserts as a left child
else
N->right=new T(k,N); //inserts as a right child
}
void test(){
AVLNode avl_root;
Node node_root;
insert(42, &avl_root);
insert(42, &node_root);
}
The downside is that the compiler will generate more code than necessary. Because it creates a new insert function for every type. This might not be a problem for you, but something worth considering. See godbolt for the generated code.
As an aside. Please please please please don't use raw pointers and new and delete. You'll be going to get so many memory leaks, especially if a pointer gets "lost" because its parent gets deleted. Consider using smart pointers like unique_ptr or shared_ptr

adding nodes to tree with unique pointer

I am very new to smart pointers and I am trying to create a doubly tree where the child nodes are pointed from the parents by a unique pointer, and the children are pointing to the parents via raw pointer. So when A parent node gets destroyed the whole sub-tree will get destroyed in the process.
class Node {
private:
Node *parent;
std::unique_ptr<Node> left;
std::unique_ptr<Node> right;
public:
Node(Node* _left, Node* _right, Node* _parent);
};
Node::Node(Node* _left, Node* _right, Node* _parent) {
parent = &_parent;
//this is where the problem starts
}
I don't understand how to point to a new node that might have a tree I want to connect. If I use make_unique I believe that will create a new node Instead of preserving the tree.
I might be totally wrong about this since I just learned smart pointers about 4 days ago (Realistically enough time to learn something).
First of all, an empty tree is possible and a default constructed node will fit well.
Parent reference will be known at the time a node is attached so, child's node parent shall be updated once a node is set as left or right child of the current tree.
It might be a good idea to receive unique_ptr as you are taking ownership of the pointer you receive. Here is an example implementation:
class Node {
private:
Node *parent = nullptr;
std::unique_ptr<Node> m_left;
std::unique_ptr<Node> m_right;
public:
void left(std::unique_ptr<Node> child) {
m_left = std::move(child);
m_left->parent = this;
}
void right(std::unique_ptr<Node> child) {
m_right = std::move(child);
m_right->parent = this;
}
};
You will use it like the following:
int main()
{
auto tree = std::make_unique<Node>();
auto subtree = std::make_unique<Node>();
subtree->right(std::make_unique<Node>());
tree->right(std::make_unique<Node>());
tree->left(std::move(subtree));
return 0;
}
I'm pretty new to unique_ptr myself, hope someone will further correct me.
BTW don't use names hat that starts with _ for your identifies, they are reserved.
I don't think you can use:
Node(Node _left, Node _right, Node _parent);
This won't allow to build the tree node by node. Instead, use:
Node(Node* _left, Node* _right, Node* _parent);
That way, you can create the first node using:
Node firstNode(nullptr, nullptr, nullptr);
From there, you can build other nodes.
To build a simple tree, with three nodes as below
N1
/ \
N2 N3
you can use:
Node* N1 = new Node(nullptr, nullptr, nullptr);
Node* N2 = new Node(nullptr, nullptr, N1);
Node* N3 = new Node(nullptr, nullptr, N1);
N1->left = N2; // Use the equivalent member function.
N1->right = N3;
I believe that you want to make the parent, left and right child public. At least, this is how I have always implemented my nodes using a struct instead:
struct Node
{
Node(std::unique_ptr<Node> _parent = nullptr,
std::unique_ptr<Node> _left = nullptr, std::unique_ptr<Node> _right = nullptr) : parent(_parent), left(_left), right(_right) {}
std::unique_ptr<Node> parent;
std::unique_ptr<Node> left;
std::unique_ptr<Node> right;
};
Someone please correct me if I am wrong.

Multiple Questions Regarding Non-Binary Trees C++

I'm currently working on building a non-binary tree that takes I/O to build the branches of the tree. I have number of issues I need to work out though and some pseudo code that I don't know how to turn into C++. For starters this is the structure of the nodes for the tree.
// Code for the structure of a node
typedef string Elem;
struct Node
{
int nodeID; // Node ID number
Elem value; // Value contains a string for each node
Node* parent; // A pointer to the parent of a node
vector<Node>* child; // A pointer to a vector which holds the children
};
Here also is the header file for the Tree class. The root_ private member is a pointer to the root of the tree and the current_ pointer points to whatever node you're currently working with. Size_ is simply an in that is incremented with the insertion or deletion of each node.
// Header file for Tree class
class AnimalTree
{
public:
AnimalTree() { root_ = NULL; }
~AnimalTree() {};
void currentNode() const; // Returns current node ID & value
void parent() const; // Return parent ID & value of current
vector<Node> children() const; // Return the ID & value of each node in current
int size() const; // Return size of tree
bool empty() const; // Return true if empty
void root() const; // Get the root ID & value
bool isLeaf() const; // Return true if current node is a leaf
bool isRoot() const; // Return true if current node is root
void searchNodeString(string val); // Search for a particular node by it's string value
private:
Node* root_;
Node* current_;
int size_;
};
Here are my questions:
Is it possible to use my node struct as input in a class function for a tree? And if so how could I implement it in something like a search function to find a specific node?
As part of a search function how would you index the child vector of a parent node? Or in C++ code, how would you search through each node in a child vector individually?
Is there a better way to use the pointers on each node to traverse the tree? I've seem to run into issues when moving from node to node when using the parent node pointer to the child vector of nodes pointer.
In C++ how do you traverse a non-binary tree? Whether it be pre-order or post-order.

Iterate over nodes in a tree

I want to make an iterator that can handle my selfmade structs:
struct node {
int n; //data element
node * parent;
node * left;
node * right;
node (int elem): n(elem){ //create root
parent = NULL; left = NULL; right = NULL;
}
node (int elem, node* p): n(elem), parent(p) { //addchild
left = NULL;
right = NULL;
}
node(int elem, node * p, node * l, node * r): n(elem), parent(p), left(l), right(r){ //insert element
}
};
First of all, is this possible? If yes: how do I start to make the iterator that traverses the tree. If not: what do you suggest for me to do if I want to access data elements in the list.
Yes.
Hint: Your iterator, when it advances, should go to the parent and figure out whether it's a left child or right child of that parent. That will tell you where to go next.
Yes, given the node structure you've outlined with a right pointer (which presumably points to the next node in traversal order), implementing a forward iterator should be quie easy.
The basic idea is something like this:
template <class Node>
class iterator {
Node *pos;
public:
iterator(Node *tree = nullptr) : pos(tree) {}
iterator operator++() { pos = pos->right; return *this; }
Node &operator*() { return *pos; }
bool operator!=(iterator const &other) const { return pos != other.pos; }
};
There is a little more than that involved in a real iterator though. In particular, you normally (always?) want to specify things like the value_type, reference_type and category of the iterator. Most of these are pretty close to pure boilerplate though, and you can handle most of them by deriving from std::iterator.
You also (typically) want to support a few more operations than I've shown here, such as post-fix increment, operator==, etc. Given this as a "framework", I think filling in those blanks should be fairly straightforward though.
I should probably also point out the existence of the Boost iterators library. This simplifies the task of implementing an iterator (somewhat) and cuts down on the boilerplate you have to write (quite a bit).

Eclipse complains about recursive function call

A simple binary search tree class declaration:
#include <vector>
#include <stdio.h>
// Provides various structures utilized by search algorithms.
// Represents an generalized node with integer value and a set of children.
class Node {
protected:
std::vector<Node*> children;
int value;
public:
//Creates a new instance of a Node with a default value=-1.
Node(){value = -1;}
//Creates a new instance of a Node with a specified value.
explicit Node(int value){this->value = value;}
virtual ~Node(){delete children;}
//Adds new Node with specified value to the list of child nodes. Multiple
//children with the same value are allowed.
//Returns added node.
virtual Node* Insert(int value);
//Removes first occurrence of a Node with specified value among children.
virtual void Remove(int value);
};
// Represents a binary search tree node with at most two children.
class BTNode: public Node {
public:
//Creates a new instance of a BTNode with a default value=-1.
BTNode():Node(){}
//Creates a new instance of a BTNode with a specified value.
explicit BTNode(int value):Node(value){}
//Adds new BTNode with specified value to the list of child nodes in an
//ordered manner, that is right child value is >= value of this node and
//left child value < value of this node.
virtual BTNode* Insert(int value);
//Removes first occurrence of a Node with specified value from the tree.
virtual void Remove(int value);
//Returns a node with specified value.
virtual BTNode* Search(int value);
};
And eclipse complains about it's definition:
BTNode* BTNode::Search(int value){
if (this->value == value) return *this;
//Determines whether value is in left(0) or right(1) child.
int child = this->value > value ? 0 : 1;
if (children[child] != NULL)
return children[child]->Search(value);
return NULL;
}
exactly where the call children[child]->Search(value) takes place with a message "method Search could not be resolved". Build runs just fine (no compilation errors whatsoever). What's the problem with that?
P.S.:Haven't tried running the code,yet. Working on it.
Search is part of the BTNode interface but it is not part of Nodes interface, children is a vector of Node* so it is not valid to call Search on a Node *. If it makes sense for Node to have a Search method then adding it to Node would fix that issue. If not then you need to rethink your design and that is probably beyond the scope of this question.
There are also a few other issues. You have:
virtual ~Node(){delete children;}
but children is not a pointer it is a std::vector<Node*>. You need to iterate over the vector and call delete each element. In Search you have this:
if (this->value == value) return *this;
but Search returns a BTNode* so it should be:
if (this->value == value) return this ;