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.
Related
in this code, I think it must do deep copy because I'm passing pointers, but it doesn't.
I think it must print 3, but print 0. what should I do to solve this? i want to have a deep copy instead of a shallow copy.
struct node{
int number = 0;
struct node* right_child = NULL;
struct node* left_child = NULL;
};
void test(struct node* node1 , struct node* node2){
node1 = node2;
}
int main(){
struct node* a1 = new struct node;
struct node* a2 = new struct node;
a2->number = 3;
test(a1 , a2);
cout << a1->number;
}
The simple C-ish way: We ad a function that recursively clones the nodes
#include <iostream>
struct node{
int number = 0;
node* right_child = nullptr; // nullptr safer than NULL
node* left_child = nullptr;
};
node * clone(const node * src){
if (src) { // there is a node. Copy it.
return new node{src->number,
clone(src->right_child), // clone right
clone(src->left_child)}; // clone left
}
else { // no node. Nothing to do and end the branch
return nullptr;
}
}
void test(node*& node1, // reference to pointer
node* node2){
delete node1; // Free storage currently used by node1
// But what of the nodes that could be in its tree?
// Think on this. Ask another question if necessary.
// this is where the simple way starts to fall down
node1 = clone(node2);
}
int main(){
struct node* a1 = new node; // wasted memory. We're about to replace it.
// need a more complicated tree to prove deep copy works
struct node* a2 = new node{3,
new node{4, nullptr, nullptr}, // a right node
nullptr}; // no left node
// a2->number = 3; done above now
test(a1 , a2);
std::cout << a1->number << ' '
<< a1->right_child->number;
delete a1; // clean up
delete a2;
}
More complicated canonical C++ way using The Rule Of Three and The Copy and Swap Idiom
#include <iostream>
#include <algorithm> // for std::swap
struct node{
int number = 0;
node* right_child = nullptr; // nullptr safer than NULL
node* left_child = nullptr;
static node * clone(node * src)
{
if (src)
{
return new node(*src);
}
return nullptr;
}
// generic constructor
node(int number = 0,
node* right_child = nullptr,
node* left_child = nullptr):
number(number),
right_child(right_child),
left_child(left_child)
{
}
//copy constructor
node (const node & src):
number(src.number),
right_child(clone(src.right_child)),
left_child(clone(src.left_child))
{
}
// assignment operator via copy and swap.
node & operator=(node src)
{
std::swap(number, src.number);
std::swap(right_child, src.right_child);
std::swap(left_child, src.left_child);
return *this;
}
// destructor
~node()
{
delete right_child;
delete left_child;
}
};
void test(node* node1,
node* node2){
*node1 = *node2; // now, thanks to all of the infrastructure above, we can
// assign nodes with the dumb old = operator. All we have to do
// is dereference the pointers.
}
int main(){
struct node* a1 = new node; // wasted memory. We're about to replace it.
// need a more complicated tree to prove deep copy works
struct node* a2 = new node{3,
new node{4, nullptr, nullptr}, // a right node
nullptr}; // no left node
// a2->number = 3; done above now
test(a1 , a2);
std::cout << a1->number << ' '
<< a1->right_child->number;
delete a1; // clean up
delete a2;
}
All of the nodes are now self-managing.
But, my opinion, the nodes should be kept as dumb as they are in the simple example. They shouldn't need to know about the tree at all. Knowledge of the tree should be in a Tree class that uses the nodes as simple containers. The Tree class should do all of the management of the nodes--creating, copying, cloning, deleting--because only it knows everything necessary to represent the tree. The users of the tree shouldn't even know nodes exist. They put data into the tree, take data out of the tree, and observe the tree without caring how the tree works and being able to so something stupid like
delete a_node;
and blow the crap out of data the tree isn't done with yet.
The Tree class preferably works iteratively rather than recursively so that the trees can be arbitrarily sized. Unless you're really careful recursing through a large tree to clone it, delete it, display it, or pretty much anything else you do with a tree runs the risk of exhausting automatic storage and causing what's typically called a Stack Overflow.
just use
void test(struct node *node1, struct node *node2) { *node1 = *node2; }
instead of
void test(struct node *node1, struct node *node2) { node1 = node2; }
and it will print 3.
This is because...
when you do node1 = node2; int the test1, you assign the pointer itself, not the structure pointed to by the pointer.When the function ends, the parameters node1 and node2 will be destroyed, so you have done nothing...
This question already has answers here:
Binary Search Tree Destructor
(6 answers)
Closed 2 years ago.
Please help me. I am stuck at this.
What am I trying to do: Binary search tree.
I am a C# developer and I learn C++ for about 2 weeks, therefore don't be so harsh with me and that's why pointers are still difficult for me.
I have a struct Node
struct Node
{
int Value;
Node* _LeftNode;
Node* _RightNode;
Node(int value)
: Value(value), _LeftNode(NULL), _RightNode(NULL)
{
}
};
and a Delete() function in BinarySearchTree.cpp
void BinarySearchТрее::Delete(Node* node)
{
if (node)
{
Delete(node->_LeftNode);
Delete(node->_RightNode);
delete(node);
node = NULL;
}
}
I want to delete the node and all of its child nodes.
When I first step in the recursion... For example:
I have two child nodes with values 10 and 19.
With recursion, I delete the nodes and set the pointers to NULL.
And here is the problem:
When I came out from the recursion the nodes are not NULL, but something strange.
And this is my problem. Why when I am in the recursion and I NULL the pointer everything is fine, but when I come out the pointer is something else.
As I talked in the comments, I think the thing is that how we can reset the pointer of the parent's(left or right child) of the initially passed node. (recursively deleting a node and its all children looks good.)
And I don't think it is possible in your current design. As Node does not contain a pointer to its parent, so there is no way to know who's the parent. node = NULL sets just the argument(local variable)'s value so it is pointless.
The C++ way would be to use std::unique_ptr.
struct Node
{
int Value;
std::unique_ptr<Node> LeftNode;
std::unique_ptr<Node> RightNode;
Node(int value)
: Value(value)
{
}
};
Then to destroy a node and all of its children, you'd call reset on the appropriate std::unique_ptr<Node>
I think what you actually want ist this:
struct Node
{
int Value;
Node* _LeftNode;
Node* _RightNode;
Node(int value)
: Value(value), _LeftNode(NULL), _RightNode(NULL)
{
}
~Node() {
delete _LeftNode;
delete _RightNode;
}
};
This way you are using the destructor to clean up recursivly.
delete nullptr is ok btw.
EDIT:
the unique_ptr<> usage in one of the other answers is probably the smarter way to do this.
Given:
struct Node
{
int data = 0;
struct Node * left = nullptr, * right = nullptr;
Node(int data) { this->data = data; }
};
This recursive function deletes a node & its childs (+ one comment):
void DeleteTree(struct Node* node) // A copy of the caller pointer
{
if (node)
{
DeleteTree(node->left); // Recur on left subtree
DeleteTree(node->right); // Recur on right subtree
delete node;
// node = nullptr; <-- This line is useless
}
}
To your wondering "but when I come out the pointer is something else":
There is no point in node = nullptr line, since when you call DeleteTree(my_node) function, node is a copy of my_mode, so when you set node = nullptr it has no effect on my_node that on exit from DeleteTree(my_node) points to a deleted, invalid object.
--
Possible solution:
#define DELETE_TREE(node) DeleteTree(node); node = nullptr; // Macro
int main()
{
struct Node* root = new Node(1);
root->left = new Node(2);
root->right = new Node(3);
root->left->left = new Node(4);
root->left->right = new Node(5);
DELETE_TREE(root->left); // root->left became nullptr
DELETE_TREE(root); // root became nullptr
return 0;
}
After DeleteTree function, the caller pointer points to an invalid object since its object already released. A possible solution is to define a DELETE_TREE Macro to "auto-nullify" the caller pointer after DeleteTree function.
--
Implementation with Modern C++ Smart Pointers:
#include <memory>
struct Node
{
int data = 0;
std::unique_ptr<Node> left, right;
Node(int data) { this->data = data; }
};
int main()
{
std::unique_ptr<Node> root;
root = std::make_unique<Node>(1);
root->left = std::make_unique<Node>(2);
root->right = std::make_unique<Node>(3);
root->left->left = std::make_unique<Node>(4);
root->left->right = std::make_unique<Node>(5);
root.reset();
return 0;
}
I am trying to create an appendToTail function which will add a node to the end of a singly linked list.
I am having trouble in adding a node if the head is NULL(the linked list is empty)
class Node {
private:
Node* next;
int data;
public:
Node(int d, Node* n = NULL)
: data(d)
, next(n)
{
}
void appendToTail(int);
//other trivial functions(getters and setters etc. ) defined and
//declared
};
void Node::appendToTail(int d)
{
Node* end = new Node(d);
Node* n = this;
if (n == NULL)
n = end;
else {
while (n->next != NULL)
n = n->next;
n->next = end;
n->next->next = NULL;
}
end = NULL;
delete end;
}
int main()
{
Node* n = NULL;
n->appendToTail(5);
std::cout << n->getData(); //getData() is a function which
//retrieves the Data member variable
}
I am expecting to get 5 but I am getting an error which appears to be caused because my node remains null.
Now with modern C++ idioms we use smart pointers instead of raw pointers, it gives you the benefit of RAII (Resource acquisition is initialization) mechanism. In addition if you want an elegant solution to your problem you should introduce a List class with which you can express more clearly the concept of an empty list. It would give something like this:
#include <memory>
#include <iostream>
class List
{
public:
class Node
{
private:
std::shared_ptr<Node> next;
int data;
public:
Node(int d):next(nullptr),data(d){}
inline int getData() const {return data;}
inline std::shared_ptr<Node> getNext() const {return next;}
friend List;
};
List():head(nullptr),tail(nullptr){}
void appendToTail(int );
inline std::shared_ptr<Node> getHead() const {return head;}
inline std::shared_ptr<Node> getTail() const {return tail;}
private:
std::shared_ptr<Node> head;
std::shared_ptr<Node> tail;
};
void List::appendToTail(int d)
{
auto newTail = std::make_shared<Node>(d);
if (head == nullptr)
{
head = tail = newTail;
}
else
{
tail->next = newTail;
tail = newTail;
}
}
int main()
{
List l;
l.appendToTail(5);
std::cout<<l.getHead()->getData();
}
But you should definitely prefer std::list<T> or std::vector<T>.
Unfortunately there several errors with your approach. Semantic errors and a logical error with your interpretation of a linked list. Let's start with your initial misunderstanding. You cannot add a new tail to an empty list. Because it is emtpy. Meaning, not yet existing. Only if some object is existing/instantiated you can add a tail. You cannot add something to not existing stuff. So your idea to start with a Node* n = nullptr cannot work logically.
Additionally you are dereferencing a nullptr (major bug). That is also the main problem of your code. Nothing works. You need an instantiated object, before you can call it's member functions.
So before you can populate the list, you need to create/instantiate it initially. So you need to explicitly create the first node in your main function with
Node* n = new Node (5)
Then the list is existing and from now on you can add new members with calling appendToTail.
There are more semantic errors in your code which have luckily no side effects.
You must not delete the 'end' variable in your function. You want to keep the newly allocated memory for the new tail. But you introduced an additional sematic error by setting 'end' to nullptr and then call delete. Deleting a nullptr is a noOp and will do nothing. So, although you have a semantic error, this will not cause any trouble.
There is more:
For a pointer to Null you should always use nullptr.
And, your
if (n == NULL)
is always false. Before that, you assigned this to n. This is never NULL. You can delete the if else. Keep the statements from the else, except the
n->next->next = NULL;
That's not necessary. The constructor did that already for you. As explained, the next 2 statements should also be elimanted.
Additionally you may want to read a little more on the concept of linked lists.
I hope I could help a little
I have a question about a function that adds a creates/adds a new node to the top of the list. Here is the set up.
A head is created in the main program. We set the list to Null
IntNode* head = new IntNode(3,NULL);
My question is about the function that adds a node to the top of the list. Assumes that there is at least one node in the list.(the one we just created)
void headInsert(IntNodePtr& head, int theData)
{
head = new IntNode(theData, head);
}
I know it creates a new node and makes the pointer already declared in the main program, which is passed, point to the new node. However I am confused about he "head" part on the parameter in the constructor(not the headInsert function). I am confused about what exactly is being passed when we pass head in the IntNode constructor above. That head sets the variable link, to point to what head is pointing to correct? ******My question is, Does it first set *link(the class variable) to point to what head is pointing to, in this case the node with the number 3 whose list points to NULL, AND THEN makes head point to the new NODE? So in other words the right part of the assignment is done first? I'm just very confused as to what is being passed in when we create the new node.
class IntNode
{
public:
IntNode( ) {}
IntNode( int theData, IntNode* theLink)
: data(theData), link(theLink) {}
IntNode* getLink( ) const { return link; }
int getData( ) const { return data; }
void setData( int theData) { data = theData; }
void setLink(IntNode* pointer) { link = pointer; }
private:
int data;
IntNode *link;
};
void headInsert(IntNodePtr& head, int theData)
{
head = new IntNode(theData, head);
}
The evaluation of the sides of the assignment -
head
and
new IntNode(theData, head)
is not ordered at all, but prior to performing the assignment, both sides have been evaluated completely.
Since neither side modifies the value of head, the results are the same regardless of the order of evaluation.
I have a problem with working with c++ pointers. I'm trying to code a splay tree by using a Node struct and Tree struct. However, upon testing, I have encountered a problem. The portion of my code that's not working is below:
struct Node {
Node* l, *r, *p;
int v;
Node() {}
Node(int _v, Node* _p) : v(_v), p(_p) {}
};
struct Tree {
Node* root;
Tree() : root(0) {}
//...
void insert(int k) {
if (!root) {
root = new Node(k, 0);
return;
}
Node* cur = new Node();
cur->v = root->v;
while (1) {
int x = cur->v;
cout << x << endl;
return;
if (k <= x) {
//cout << x << endl;
//return;
if (!cur->l) {
cur->l = new Node(k, cur);
//splay(cur->l);
return;
} else cur = cur->l;
} else {
if (!cur->r) {
cur->r = new Node(k, cur);
//splay(cur->r);
return;
} else cur = cur->r;
}
}
}
//...
};
int main() {
Tree t = Tree();
t.insert(1);
t.insert(5);
return 0;
}
First, I inserted a node with value 1 in the tree; since there was no root, the tree assigned its root as a new node with value 1. Then, when I inserted 5 into the tree, something weird happened. If you leave the code like it is (keeping the first cout), then it will print out 1 for x. However, if you comment out the first cout and return and uncomment the second cout and return, you'll find that it prints out a random garbage number for x, even though no modifications were made. Can somebody tell me what's wrong?
C++ does not initialize class members automatically.
struct Node {
Node* l, *r, *p;
int v;
Node() {}
Node(int _v, Node* _p) : v(_v), p(_p) {}
};
When you create a new node in your code C++ allocates a piece of memory for the Node but it will not clear it. So the values of l, r & p will be whatever was there.
In your algorithm the tests: if (!cur->r) & (!cur->l) currently fail because there is uninitialized garbage in the nodes and not NULL.
As a result when you try to insert the second node the algorithm thinks that there is a valid node to the right of root. And tries to read the memory there and the value there which is the junk x you see. Depending on the value of the junk it may also crash for some people running the code :)
Also I'm 99.9% certain that Node* cur should be a pointer to a Node in the tree and not a new node so:
Node* cur = new Node(); cur->v = root->v; is wrong and should be Node* cur = root;
Proper Initialization -
In c++11 you can do:
struct Node {
Node* l = nullptr;
Node *r = nullptr;
Node *p = nullptr;
int v = 0;
Node() {}
Node(int _v, Node* _p) : v(_v), p(_p) {}
};
Otherwise
struct Node {
Node* l;
Node *r;
Node *p;
int v;
Node() : l(NULL), r(NULL), p(NULL), v(0){}
Node(int _v, Node* _p) : l(NULL), r(NULL), p(_p), v(_v) {}
};
You should initialize members of a class in the same order they were defined.
Now there are a lot of other things that are problematic in the code:
Tree seems to allocate lots of nodes but does not release any memory. (easiest to just use unique_ptr for l and r and root Node)
Is tree the owner of subnodes? Or should it be Node owning and allocating left and right? (goes away if you use std::unique_ptr for left and right)
You are not initializing the members in the order they are defined. This can cause all kind of errors. (since the compiler reorders initialization without telling you)
Node and Tree handle raw pointers but do not define a proper operator=, copy ctor (or delete them) (goes away if you use unique_ptr)
Tree is missing a dtor to clean allocated memory (goes away if you use unique_ptr)