I am working with Trees, for practice purpose. Precisely Binary Search Trees currently. I have a general Tree class which I use to solve the BST problems.
So, I come across a problem tp convert the given tree (BST) into a Doubly linked list.
I am able to successfully convert the tree into a DLL. But, the problem is when I call the destructor.
Since, I am allocating memory to the nodes of my Tree, I also wish to free the memory.
This is the destructor of the Tree class ::
~Tree() {
delete root;
root = NULL;
}
And this is the destructor of the Node class::
~Node() {
delete left;
delete right;
left = NULL;
right = NULL;
}
So, the program crashes at the end!
To my understanding since the destructors that I have written kindof recursively delete all the nodes of the tree, and when the Tree is converted to a DLL, the left and right pointers indeed point to each other, so, during the call to the destructor, the destructor tries to delete the node which has already been deleted and that pointer has not been set to NULL.
How do I overcome this? Since destructor overloading is not permitted.
Is there any way I could prevent this runtime error??
This is the code :: http://ideone.com/SDkXY9
(Ideone doesn't print the output, I don't know why!)
The problem here is that you are trying to delete neighbours from whom the chained destruction is initiated. You will have to add a flag which you can check in the destructor for which neighbour is sane to delete.
~Node() {
_isDestructing = true;
if (!left->_isDestructing) delete left;
if (!right->_isDestructing) delete right;
left = NULL;
right = NULL;
}
Related
I defined a binary node class in c++.
Now, I'm tryin to implement the destructor of the binary node, which basically should just free the heap space I allocated to its descendants.
So here's what I have done:
virtual ~BinaryNode(){
freeSpace(left);
freeSpace(right);
}
where freeSpace(Node* node) is a private auxiliary function I want to implement in the class.
My question is about 2 options:
void freeSpace(Node* curr){ //Option 1
if(curr->left)
freeSpace(curr->left);
if(curr->right)
freeSpace(curr->right);
delete curr;
}
void freeSpace(Node* curr){ //Option 2
if(curr->left)
left->freeSpace(curr->left);
if(curr->right)
right->freeSpace(curr->right);
delete curr;
}
What option would be the correct way to implement?
Thanks in advance.
I would do this instead:
virtual ~BinaryNode() {
delete left;
delete right;
}
BinaryNode* root = new BinaryNode();
...
delete root;
Edit 1:
As delete calls the destructor of the object then deallocate the memory, you can utilise delete to recursively delete all of the nodes starting from the leaves. Thus, there is no need to create your own custom function to traverse the whole tree and call delete on each node.
Edit 2: Used #YvesDaoust suggestion and remove nullptr check
I am a beginner in programming and currently learning about bst. I have found several functions which delete bst, but they seemed to me overcomplexed. So i am interested if my own code cleans all the memory, every pointer and value of a binary search tree.
void deletebst (Node*& node)
{
if (node!=NULL)
{
deletebst (node->ldes); //deletes a left descendant recursively
deletebst (node->rdes); //same but with the right one
delete node; //deletes the value which a pointer 'node' points to
node=NULL; //sets a pointer 'node' to NULL, so deletes a pointer itself
}
}
As pointed out in the comments section, there's no need to set node = NULL, after it's been deleted. The rest of the code is fine. Cheers!
I made a binary tree class which holds:
int value, BinaryTree* left, BinaryTree* right.
class BinaryTree {
private:
int value;
BinaryTree* left;
BinaryTree* right;
bool isVisited;
public:
BinaryTree();
BinaryTree createComplete(int n);
~BinaryTree();
}
My destructor is :
BinaryTree::~BinaryTree() {
delete left;
delete right;
}
When running in clion it works perfectly, but in my terminal I get
a segfault (core dumped). Everywhere I looked people claimed that this should be the destructor. Any elaboration would help!
I am not a stackoverflow expert , I updated my ~BinaryTree function to still gets a segfault :
BinaryTree::~BinaryTree() {
if (right != NULL) {
delete right;
}
if (left != NULL) {
delete left;
}
}
First of all your current implementation is not that of a complete tree.
It is a node, thus I suggest renaming it to BinaryTreeNode and using it to construct a new class BinaryTree, that keeps track of the root and allows you to recursively deallocate the tree.
Having said that your destructor most likely segfaults because you are blindly attempting to delete a pointer.
First make sure you initialize left and right to nullptr.
Then you do if(left != nullptr) { delete left }
Without seeing your constructor, I assume you don't initialize your node's children to NULL. That might mean that the uninitialized nodes left and right at the bottom leaves have a random value in them. When the destructor runs, it will try to free the memory that the random garbage in the nodes point to.
Try initializing your child nodes to NULL when ctoring nodes, then making a check for it like monoceres suggested. It will also be good to set the pointer to NULL after delete to avoid situation of erronous double delete
So after debugging I noticed that the every right child is loosing it's nodes , which while going in a pre order traversal is fine , but when deleting it casuing the problem , thanks for the help every one !
Is it necessary to assign all pointers in a class to NULL before deleting? I mean, from example, for the following snippets of a function that deletes a node in a binary search tree,
1.
Node *temp = parent->left;
parent->left = temp->left;
delete temp;
2.
Node *temp = parent->left;
parent->left = temp->left;
temp->left = NULL;
delete temp;
Is the line
temp->left = NULL;
necessary? Some tutorials do it while others don't.
Depends. If the destructor of Node deletes the child nodes, then it is necessary. If it doesn't then that line is not necessary. Yet some tutorials might include it solely in order to promote a good programming style. If you introduce a bug to your program and use the pointer after deleting it, then having a null pointer will give you an error message instead of undefined behavior. You might also want to add the line temp = NULL; if you want to follow this style more rigorously.
It probably isn't necessary. No code can safely dereference temp after it has been deleted. As noted by Ralph Tandetzky however, it is valid if Node has a destructor which operates on its left member
One other possible benefit of NULLing its members is to catch invalid access more predictably.
No the line
temp->left = NULL;
is not necessary. In many cases it can be helpful if you force yourself to add this line after every delete because you will be able to easily identify the error if your program crashes and you see the pointer set to zero.
It is not strictly necessary, this is a measure designed to help in debugging. Imagine that you left that pointer without a NULL (e.g., in a linked list), and somehow you stepped into memory that has been freed, but its contents have not been deleted. Then there exists the danger that you can follow that pointer, without actually knowing whether it still exists or not, and maybe trying to delete that memory when it has already been deleted.
So, it is just safety, not something designed to achieve any functionality.
There is another (complementary) way to achieve the same: detect whether the memory being accessed has been deleted or not. For example:
class Node {
public:
Node()
{ status = 0xf00dbeef; }
~Node()
{ status = 0xdeadbeef; }
// ...
private:
int status;
bool isAlive() const
{ return ( status == 0xfoodbeef ); }
};
This way, you can always assure that you are using memory that was correctly allocated, i.e. it was a) not freed and b) it actually stores a Node object.
Hope this helps.
No, this is not needed, but if the memory pointed to by left pointer is owned by the object, you should delete it in turn.
I have recently managed to get a stack overflow when destroying a tree by deleting its root 'Node', while the Node destructor is similar to this:
Node::~Node(){
for(int i=0;i<m_childCount;i++)
delete m_child[i];
}
A solution that come up into my mind was to use own stack. So deleting the tree this way:
std::stack< Node* > toDelete;
if(m_root)
toDelete.push(m_root);
while(toDelete.size()){
Node *node = toDelete.top();
toDelete.pop();
for(int i=0;i<node->GetChildCount();i++)
toDelete.push(node->Child(i));
delete node;
}
But in there the std::stack::push() may throw an exception. Is it possible to write an exception free tree destruction? How?
EDIT:
If anybody is interested here is an exception free non-recursive code inspired by the algorithm pointed out by jpalecek:
Node *current = m_root;
while(current){
if(current->IsLeaf()){
delete current;
return;
}
Node *leftMostBranch = current;// used to attach right subtrees
// delete all right childs
for(size_t i=1; i<current->GetChildCount(); i++){
while(!leftMostBranch->Child(0)->IsLeaf())
leftMostBranch = leftMostBranch->Child(0);
delete leftMostBranch->Child(0);
leftMostBranch->Child(0) = current->Child(i);
}
// delete this node and advance to the left child
Node *tmp = current;
current = current->Child(0);
delete tmp;
}
note: Node::IsLeaf() is equivalent to Node::GetChildCount()!=0.
I just had this as an interview question.
And I must admit this is one of the hardest things I had to solve on the fly.
Personally I don't think it's a good question as you may know the trick (if you have read Knuth) in which case it becomes trivial to solve but you can still fool the interviewer into making him/her think you have solved it on the fly.
This can be done assuming that the node stores child pointers in a static structure. If the node stores child pointers in a dynamic structure then it will not work, as you need to re-shape the tree on the fly (it may work but there is no guarantee).
Surprisingly the solution is O(n)
(Technically every node is visited exactly twice with no re-scanning of the tree).
This solution uses a loop (so no memory usage for stack) and does not dynamically allocate memeroy to hold nodes that need to be deleted. So it is surprisingly effecient.
class Node
{
// Value we do not care about.
int childCount;
Node* children[MAX_CHILDREN];
};
freeTree(Node* root)
{
if (root == NULL)
{ return;
}
Node* bottomLeft = findBottomLeft(root);
while(root != NULL)
{
// We want to use a loop not recursion.
// Thus we need to make the tree into a list.
// So as we hit a node move all children to the bottom left.
for(int loop = 1;loop < root->childCount; ++loop)
{
bottomLeft->children[0] = root->children[loop];
bottomLeft->childCount = std::max(1, bottomLeft->childCount);
bottomLeft = findBottomLeft(bottomLeft);
}
// Now we have a root with a single child
// Now we can delete the node and move to the next node.
Node* bad = root;
root = root->children[0];
delete bad; // Note the delete should no longer destroy the children.
}
}
Node* findBottomLeft(Node* node)
{
while((node->childCount > 0) && node->children[0] != NULL))
{ node = node->children[0];
}
return node;
}
The above method will work as long as their is always a children[0] node (even if it is NULL). As long as you do not have to dynamically allocate space to hold children[0]. Alternatively just add one more pointer to the node object to hold the delete list and use this to turn the tree into a list.
This is what all garbage collectors struggle with. However, the best thing you can do (IMHO) is to pray for enough memory for the stack, and your prayers will be heard 99.99999% of the time. Should it not happen, just abort().
BTW if you are interested, there is a solution to traverse long (and deep) trees without allocating much memory.
Why is the original code throwing an exception? I'm guessing you are doing something like using the same node object in multiple places in the tree. Stack overflows are rarely caused by normal expected situations. Stack overflows are not a problem, they are the symptom of a problem.
Rewriting the code differently won't fix that; you should just investigate & fix the error.
Is it possible to write an exception free tree destruction? How?
Perhaps this (untested code):
void destroy(Node* parent)
{
while (parent)
{
//search down to find a leaf node, which has no children
Node* leaf = parent;
while (leaf->children.count != 0)
leaf = leaf->chilren[0];
//remember the leaf's parent
parent = leaf->parent;
//delete the leaf
if (parent)
{
parent->children.remove(leaf);
}
delete leaf;
} //while (parent)
}