So I am writing a BST node removal function, but I cannot figure out a problem:
I have this node-data structure:
struct webentry
{
char * topic;
char * keyword;
char * url;
char * summary;
char * review;
int rating;
};
The problem occurs at the point where the node has a two children and its right child has a left child (meaning I need to find the inorder successor and replace it with):
else if ((root->right != NULL) && (root->left !=NULL) && (root->right->left !=NULL))
{
node*tempr = root->right;
node*templ = root->left;
delete root->data.topic;
delete root->data.keyword;
delete root->data.url;
delete root->data.summary;
delete root->data.review;
delete root;
root = findis(tempr);
root->right = tempr;
root->left = templ;
return 1;
}
I wrote a findis function to deal with the inorder successor situation:
node* program4::findis(node* &root)
{
/* cout to figure out what's going on
cout<<"Topic: "<<root->data.topic<<endl; Finds it here
cout<<"Keyword: "<<root->data.keyword<<endl; Finds it here
*/
if ((root->left==NULL) && (root->right ==NULL))
{
node*temp = root;
delete root;
root = NULL;
/*
cout<<"Topic: "<<temp->data.topic<<endl; Empty
cout<<"Keyword: "<<temp->data.keyword<<endl; Finds
*/
return temp;
}
else if ((root->left == NULL) && (root->right != NULL))
{
node*temp= root;
node*tempn = root->right;
delete root;
root = tempn;
/*
cout<<"Topic: "<<root->data.topic<<endl;
cout<<"Keyword: "<<root->data.keyword<<endl;
*/
return temp;
}
else
{
return findis(root->left);
}
The problem is: When the node inorder successor returns, topic part becomes NULL, everything else stays the same. I have used GDB to figure out where it deletes the contents of topic, and it is right where I marked, right after the if condition meets and deletes the root in the findis function. It doesn't delete any other data of the the node, I don't know why it even deletes it. Can anyone see what is happening here?
Related
I'm having difficulty deleting a node in my binary search tree. The delete function is part of my Node class, and my findMin function is as well. Below is my delete function...
/**********************************************
* Delete
**********************************************/
node* node::Delete(node *root, string stuff)
{
//node *temp;
if (root == NULL) // Searches for value in tree
return NULL;
if (stuff < root->val) // String is in left subtree
root->left = Delete(root->left, stuff);
else if (stuff > root->val) // String is in right subtree
root->right = Delete(root->right, stuff);
else
{ // No children
if ((root->left == NULL) && (root->right == NULL))
{
delete(root);
root = NULL;
}
else if ((root->right == NULL) && (root->left != NULL)) // One left child node
{
node *temp = root;
root = root->left;
delete temp;
temp = NULL;
}
else if ((root->left == NULL) && (root->right!= NULL)) // One right child node
{
node *temp = root;
root = root->right;
delete temp;
temp = NULL;
}
else // Two children
{
node *temp = findMin(root->right); // Finds smallest value in right subtree
root->val = temp->val;
root->right = Delete(root->right, temp->val);
}
}
return root;
}
Below is my Destructor, which is giving me a SIGABRT (I'm using Xcode)
/**********************************************
* Destructor
**********************************************/
node::~node()
{
if (left != NULL) delete left;
if (right != NULL) delete right;
}
What my code is actually doing is not only deleting the node I intend to delete, but its child node. What could I be doing wrong? Is it an error with memory allocation? Is it an error with how I set the value to the child node?
You need to null your pointers to left and right before deleting a node.
You call:
node *temp = root;
root = root->left;
delete temp;
temp = NULL;
When you "delete temp" you are deleting a node which still points to root->left and root->right and your destructor insures they are also removed. You should instead do something like this:
node *temp = root;
root = root->left;
temp->left = NULL;
temp->right = NULL;
delete temp;
temp = NULL;
Also in your destructor you don't need to check if they are equal to null since delete already preforms this check.
I have a recRemove function that recursively removes the given node. I also have a findMin function that finds the smallest node in the BST. I'm having trouble merging the two so that I can remove the smallest(or largest) node. This is what I tried doing but it just returned garbage: Full code: https://pastebin.com/HCVsUZ4S
//remove min node in BST
node * extractMin()
{
return recRemove(root, findMin(root));
}
//delete node from tree
node * recRemove(node * root, double data)
{
//3 cases: no children, one child, 2 children
if (root == NULL)
{
return NULL;
}
else if (data < root->data)
{
root->left = recRemove(root->left, data);
}
else if(data > root->data)
{
root->right = recRemove(root->right, data);
}
else
{
if (root->right == NULL && root->left == NULL) //no children
{
delete root;
root = NULL;
return root;
}
else if(root->left == NULL) //only right child
{
temp = root;
root = root->right;
delete temp;
return root;
}
else if(root->right == NULL) //only left child
{
temp = root;
root = root->left;
delete temp;
return root;
}
else //2 children
{
temp->data = findMin(root->right);
root->data = temp->data;
root->right = recRemove(root->right, temp->data);
}
}
return root;
}
//find min node in BST
double findMin(node * p)
{
if(p == NULL)
{
return -1;
}
else
{
//in a balanced BST the minimum node is the leftmost node so,
//we traverse the left subtree until we get to the leftmost node and return and remove it.
temp = p;
while(temp->left != NULL)
{
temp = temp->left;
}
return temp->data;
}
}
sorry , can't write comments yet (will delete this later)
Where is temp defined? If it is a global variable than this is probably the issue...
Edit:
Have now seen the pasebin.....
temp is a member variable. Change it to a local variable.
Make sure to delete it before leaving the function. (best use std::unique_ptr<>)
I have these functions to remove a node from my binary search tree:
bool collection::removeFromTree(const char name[])
{
for (treeNode * curr = root; curr;)
{
int8_t result = strcmp(name, curr->item->getName());
if (result == 0)
{
deleteNode(curr);
return true;
}
else if (result < 0)
curr = curr->left;
else if (result > 0)
curr = curr->right;
}
return false;
}
void collection::deleteNode(treeNode *& goneNode)
{
//if it's a leaf
if (!goneNode->left && !goneNode->right)
{
delete goneNode; //node's destructor gets invoked
goneNode = nullptr;
}
//if it has right child
else if (!goneNode->left)
{
goneNode = goneNode->right;
}
//if it has left child
else if (!goneNode->right)
{
goneNode = goneNode->left;
}
//if it has both children
else
{
treeNode * prev = nullptr;
treeNode * curr = goneNode->right;
while (curr->left)
{
prev = curr;
curr = curr->left;
}
//prev points to the copy over data
delete goneNode->item;
if (!prev)
{
goneNode->item = curr->item;
goneNode->right = curr->right;
curr->item = nullptr;
}
else
{
goneNode->item = curr->item;
curr->item = nullptr;
prev->left = curr->right;
}
}
}
This runs fine, but when I try to list all the elements in my tree after deleting a node (with these functions):
void collection::displayByName() const
{
std::cout << std::endl
<< "========================================" << std::endl;
//display the tree inorder
listAll(root);
}
void collection::listAll(const treeNode * const & root) const
{
if (root)
{
std::cout << *(root->item) << std::endl
<< "========================================" << std::endl;
listAll(root->left);
listAll(root->right);
}
}
I receive this error:
And when I quit the program after deleting a node (invoking these destructors):
collection::~collection()
{
delete root;
}
collection::treeNode::~treeNode()
{
delete left;
delete right;
}
I recieve this error:
Any suggestions would be greatly appreciated because I see no reason for my listAll() function to be calling nodes that I've already deleted.
By the way, this is my struct for my treeNode:
struct treeNode
{
treeNode();
treeNode(vendor *& item);
~treeNode();
vendor * item;
treeNode *left, *right;
};
treeNode * root; //the bst
hashNode ** table; //the hash table
uint8_t capacity;
uint8_t size;
const static uint8_t INIT_CAP = 20;
When you need to remove a node from a singly linked list or a tree, I find using a pointer to pointer is handy. Namely, if we have a treeNode** ptr;, then *ptr is the pointer to our node. So, if ptr = &root, then *ptr = nullptr sets root to nullptr.
I removed the deleteNode function and threw its logic in the removeFromTree function.
bool collection::removeFromTree(const char name[])
{
treeNode** ptr = &root;
Instead of being a pointer to treeNode, ptr will point to a treeNode* inside the tree structure. This way, we can modify the pointer that led us to the current node. The lines marked //same as before have the same logic you were using, just possibly modified to account for the fact ptr has another level of dereferencing to do.
int result; //same as before
while (*ptr) //While we haven't hit a dead end
{
result = strcmp(name, (*ptr)->item->getName()); //same as before
if (result < 0) //same as before
ptr = &((*ptr)->left); //same as before
else if (result > 0) //same as before
ptr = &((*ptr)->right); //same as before
else //begin deleteNode() logic
{
if ((*ptr)->left && (*ptr)->right) //two children
{
Here, we use pointers to member because the alternative was a conditional operator on every line. If a node has two children, we need to find either the rightmost node on the left side, or the leftmost node on the right side. That's the node we can replace the current node with.
treeNode* treeNode::*dir = some_condition ? &treeNode::right : &treeNode::left; //pointer to treeNode member of type treeNode*
treeNode* treeNode::*ndir = some_condition ? &treeNode::left : &treeNode::right; //pointer to treeNode member of type treeNode*
dir now either points to left or right, which is the direction we are searching the tree for. ndir is the opposite direction. So, if we want the rightmost node on the left side, (*ptr)->*dir == (*ptr)->left and (*ptr->*ndir == (*ptr)->right. If we want the leftmost right node, it would be reversed. This is just a more complicated way to do less work, really. It shouldn't be hard to remove. some_condition is just either true or false. true means the left side of the tree (from the current node) loses a node, and false means the right side does.
treeNode** replacement = &((*ptr)->*ndir); //the node to replace the current one with
while ((*replacement)->*dir) //While we aren't at the edge
replacement = &((*replacement)->*dir);
This loops until *replacement is the node we need to replace *ptr with.
treeNode* rep_branch = (*replacement)->*ndir; //If the replacement node had a child, this is now it
(*replacement)->left = (*ptr)->left; //Copy current to replacement
(*replacement)->right = (*ptr)->right; //Copy current to replacement
(*ptr)->left = nullptr; //null out current in case of destructor
(*ptr)->right = nullptr; //null out current in case of destructor
Now, the replacement node is pointing to the node-to-be-deleted's children, and our soon to be expired node has no children anymore. Now, it's safe to delete the unwanted node. If the node class had a destructor to delete its children, the left and right pointers were set to nullptr just in case.
delete *ptr; //delete unwanted node
*ptr = *replacement; //replacement node has taken the unwanted node's place in the tree
*replacement = rep_branch; //The replacement's child takes its own place
}
This completes the tree's structure. Wherever the unwanted node was, the replacement node has taken its place. And because the replacement node was required to be an edge node, it had at most one child. We just replace it with the child.
else if ((*ptr)->left) //one child on left
{
treeNode* current = *ptr;
*ptr = (*ptr)->left; //replace current with left
current->left = nullptr; //null out for safety
delete current;
}
else if ((*ptr)->right) //one child on right
{
treeNode* current = *ptr;
*ptr = (*ptr)->right; //replace current with right
current->right = nullptr; //null out for safety
delete current;
}
else //no children
{
delete *ptr;
*ptr = nullptr;
}
return true; //yay it's over
}
}
return false; //never found it
}
The rest is fairly straightforward, just replacing easier nodes and returning. Hopefully this gives you some ideas about how to approach problems like this, and the occasional uses of some of these structures. This is what I meant about using treeNode** over treeNode* for operations like this.
I am getting Exception when running the BST Deletion. Below is my code snippet:
Bst::node * Bst::del(node *root, int num)
{
if (root == NULL)
{
return root;
}
else if (num < root->data)
{
root->left = del(root->left, num);
}
else if (num > root->data)
{
root->right = del(root->right, num);
}
else
{
if (root->left == NULL)
{
node * tmp = root;
root = root->right;
delete tmp;
}
else if (root->right == NULL)
{
node * tmp = root;
root = root->left;
delete tmp;
}
else if (root->left == NULL && root->right == NULL)
{
delete root;
root = NULL;
}
else
{
node *tmp = root;
tmp = findMin(root->right);
root->data = tmp->data;
root->right = del(root->right, tmp->data);
}
}
return root;
}
void Bst::del(int num)
{
del(root, num);
}
Everything works fine when I am deleting the other nodes but when I delete the root node itself then the function void Bst::del(int num) gets the garbage value from the function Bst::node * Bst::del(node *root, int num). The error gets resolved when I rewrite my function as
void Bst::del(int num)
{
root = del(root, num);
}
Question 1. Why it works when I delete the middle nodes or any other node except the root node. While debugging I found that even root was getting deleted properly when the function Bst::node * Bst::del(node *root, int num)was executing but when the call returned to the void Bst::del(int num) then the value of root was not getting retained and was garbage.
Question 2: Why the error got fixed when I stored the returned value in variable root?
While deleting a BST node using recursion, you must track the root node, which you're doing correctly as
root->left = // ... and root->right = ...
However when call reaches to caller after unwinding the stack, the root may get modified ( case when you delete the root itself )
This hopefully answers both of your questions
I am trying to finish the delete function
Here is the pseudo code, notice the end.
I don't know if the pseudo code is wrong though.
Here is how I interpreted it:
Node* minNode = Minimum(toDelete->right);
int tmp = 0;
tmp = minNode->val;
// delete(&tmp);
free(minNode);
minNode=NULL;
toDelete->val=tmp;
Except once it deletes it, it starts filling a trillion zeroes when printing.
Is what I am doing make any sense?
The rest of the code I have is right, or I think so anyway. It only screws up in this scenario.
Here's the minimum function as well
Node* BST::Minimum(Node *curr) {
// if (curr->left != NULL) {
// return(Minimum(curr->left));
// }
// return curr;
Node* node = curr;
while (node->left != NULL) {
node = node->left;
}
return node;
}
You want to first of all search the tree so see if the node you want to delete is there.
if it is there, you want to check for three casing:
1: when you want to delete a Node that have no child.
: in this case you just delete the said node as it does not have any child.
2: when you want to delete a node that has either left of right child
: in this case you set the left or right child to the parent of the node you want to delete
3: when you want to delete a node with two children
: in this case you have to find the successor of the node you are to delete, then swap the successor with the delete node.
public Boolean delete(int key)
{
Node current = root;
Node parent = root;
//search for node here
while(current->key != key)
{
parent = current; //save the parent the nodes as you loop through it
if(key < current->key)
current = current->left
else
current = current->right
//if you do not find the key return false
if(current==null)
return false;
}
//case 1 start here:
//check if the said node has no child. in this case we are looking at current
if(current->left ==null && current->right == null)
{
//check if the node you want to delete is the root
if(current == root)
root = current
else
{
if(parent.key > current->key)
parent-left = null;
else
parent->right = null;
}
}//case 2 here:
//check to see if the node has either left or right child
else if(statement for checking left here)
{
//check for the case where your delete a root
//set the the parent of the current->left to be current->parent
}
else if(statement for checking right here)
{
//check for the case where your delete a root
//set the the parent of the current->right to be current->parent
}
else
{
//create a node successor and give it the successor you found
Node successor = findSuccessor(Node NodeToDel);
//check for root being the node you want to delete
if(root == successor)
root = successor;
else
{
//navigate left, set parent->left = successor
//navigate right, set parent->right = successor
//with the nature of the findSuccessor() algorithm i have,
//i will have to code one more line:
// give succeccor->left = current->left;
}
}
}
I hope this will help in some way.