I'm trying to implement a removal algorithm discussed in a textbook for a binary search tree in a program, but the book is scant on details for some of the functions described so I've guessed at their meaning and implemented the functions it specified and some of my own. The problem I'm having is with the removeNode function on handling the 0-1-2 children cases.
In the book it specifies the following pseudocode for removeNode
removeNode(N: BinaryNode)
{
if(N is a leaf)
Remove N from the tree
else if (N has only one child C)
{
if(N was a left child of its parent P)
Make C the left child of P
else
Make C the right child of P
}
else //Node has two children
{
//Find S, the node that contains N's inorder successor
//Copy the item from node S into node N
//Remove S from the tree by using the previous
//technique for a leaf or a node with one child
}
In this function, how do you make C a child of P? given a single node with nothing to point back to the parent what can you do to figure out who the parent of the tree is? Usually you need a trailing node to keep track of that but due to the books 'final draft' I suspect that wasn't what they were implying.
'Final Draft'
removeNode(nodePtr: BinaryNodePointer): BinaryNodePointer
{
if(N is a leaf)
{
//Remove leaf from the tree
delete nodePtr
nodePtr = nullPtr
return nodePtr
}
else if (N has only one child C)
{
if(N was a left child of its parent P)
nodeToConnectPtr = nodePtr->getleftChildPtr() //<---I assume this means nodePtr->left
else
nodeToConnectPtr = nodePtr->getRightChildPtr() //<--nodePtr->right?
delete nodePtr
nodePtr = nullptr
return nodeToConnectPtr
}
else //Node has two children
{
//Find the inorder succesor of the entry in N: it is in the left subtree rooted
//at N's Child
tempPtr = removeLeftMosstNode(nodePtr->getRightChild(), newNodeValue)
nodePtr->setRightChildPtr(tempPtr) //<--nodePtr->right = tempPtr?
nodePtr->setItem(newNodeValue) // nodePtr->vendorData = newNodeValue?
return nodePtr
}
This is the implementation I came up with based off the aforementioned design. I know some parts are wrong but I wasn't sure what else I could do to fix them. Could anyone suggest a fix the child cases and any other problems I might have missed?
My Implementation
aBst::treeNode * aBst::removeNode(aBst::treeNode * nodePtr)
{
//This functions deletes a node and then returns the pointer to the child to take the place of deleted child
aVendor * tempVendorPtr;
treeNode * nodeToConnectPtr, *tempPtr;
//The node passed is the node that needs to be removed
if (nodePtr->right == NULL && nodePtr->left == NULL) //----No Child----
{
delete nodePtr;
nodePtr = NULL;
return nodePtr;
}
else if ((nodePtr->right != NULL) != (nodePtr->left != NULL))//----One Child----
{
if (nodePtr->left != NULL)//left child
{
nodeToConnectPtr = nodePtr->left; //Wrong
}
else if (nodePtr->right != NULL) //right child
{
nodeToConnectPtr = nodePtr->right; //Wrong
}
delete nodePtr;
nodePtr = NULL;
return nodeToConnectPtr;
}
else //-----Two Child-----
{
//find minimum value of right subtree, stores the pointer to the vendorData it carries through the parameter and calls removeNode
tempPtr = removeLeftMostNode(nodePtr->right, tempVendorPtr);
nodePtr->vendorData = tempVendorPtr;
nodePtr->right = tempPtr;
return nodePtr;
}
}
All functions
int aBst::countKids(aBst::treeNode * subTreePtr)
{
if (subTreePtr == NULL) //Empty Tree
{
return -1;
}
else if (subTreePtr->right == NULL && subTreePtr->left == NULL) //----No Child----
{
return 0;
}
else if ((subTreePtr->right != NULL) != (subTreePtr->left != NULL))//----One Child----
{
return 1;
}
else if ((subTreePtr->right != NULL) && (subTreePtr->left != NULL))//----Two Child----
{
return 2;
}
//Something unexpected occurred
return -1;
}
bool aBst::remove(char nameOfVendor[])
{
bool failControl = false;
removeValue(root, nameOfVendor, failControl);
return failControl;
}
aBst::treeNode * aBst::removeValue(aBst::treeNode * subTreePtr, char nameOfVendor[], bool& success)
{
//Note: the subTreePtr should be root in initial call
treeNode * tmpPtr;
char name[MAX_CHAR_LENGTH];
//Make sure passed success bit is false
success = false;
subTreePtr->vendorData->getName(name);
if (subTreePtr == NULL) //Empty Tree
{
success = false;
return NULL;
}
else if (strcmp(name, nameOfVendor) == 0) //Evaluates to true if there is a match
{
subTreePtr = removeNode(subTreePtr);
success = true;
return subTreePtr;
}
else if (strcmp(name, nameOfVendor) > 0) // Go left
{
//Protects algorithm from bad data crash
if (subTreePtr->left == NULL)
{
return subTreePtr;
}
tmpPtr = removeValue(subTreePtr->left, nameOfVendor, success);
subTreePtr->left = tmpPtr;
return subTreePtr;
}
else // Go Right
{
//Protects algorithm from bad data crash
if (subTreePtr->right == NULL)
{
return subTreePtr;
}
tmpPtr = removeValue(subTreePtr->right, nameOfVendor, success);
subTreePtr->right = tmpPtr;
return subTreePtr;
}
//For loop was broken and function returns false
return subTreePtr;
}
aBst::treeNode * aBst::removeNode(aBst::treeNode * nodePtr)
{
aVendor * tempVendorPtr;
treeNode * nodeToConnectPtr, *tempPtr;
//The node passed is the node that needs to be removed
if (nodePtr->right == NULL && nodePtr->left == NULL) //----No Child----
{
delete nodePtr;
nodePtr = NULL;
return nodePtr;
}
else if ((nodePtr->right != NULL) != (nodePtr->left != NULL))//----One Child----
{
if (nodePtr->left != NULL)//left child
{
nodeToConnectPtr = nodePtr->left;
}
else if (nodePtr->right != NULL) //right child
{
nodeToConnectPtr = nodePtr->right;
}
delete nodePtr;
cout << "called\n";
nodePtr = NULL;
return nodeToConnectPtr;
}
else //-----Two Child-----
{
//find minimum value of right subtree, stores the pointer to the vendorData it carries through the parameter and calls removeNode
tempPtr = removeLeftMostNode(nodePtr->right, tempVendorPtr);
nodePtr->vendorData = tempVendorPtr;
nodePtr->right = tempPtr;
cout << "\nleaving Two Child\n";
return nodePtr;
}
}
aBst::treeNode * aBst::removeLeftMostNode(aBst::treeNode * nodePtr, aVendor*& vendorDataRef)
{
if (nodePtr->left == NULL)
{
//Target acquired
vendorDataRef = nodePtr->vendorData;
return removeNode(nodePtr);
}
else
return removeLeftMostNode(nodePtr->left, vendorDataRef);
}
I think you have a similar problem as I do. What you're doing when there is only one child, is just setting the pointer to branch to the right or left respectively. But you need to replace the node with a node from that subtree. This can be done by searching for the minimum node in the left subtree and replacing the node you wanted to remove with this minimum node. Then you need to remove the node you just inserted to prevent node duplication. That's the theory anyway. I haven't managed to implement it correctly myself.
edit: I removed the link again. I saw that it is considered bad etiquette to ask something in an answer. Shame on me /o.
Related
I'm writing out a BST that holds some data, and I'm trying to implement the function for removing a single element from the BST.
Currently, my recursiveDelete() function looks as follows, and it works! Depending on your definition of works. It takes some other nodes along with it.
template <typename ItemType, typename KeyType>
BNode<ItemType>* BinarySearchTree<ItemType, KeyType>::recDeleteNode(KeyType key, BNode<ItemType> *subtree)
{
if (subtree == nullptr)
{
return subtree;
}
else if (subtree->getItem() > key)
{
subtree->setLeft(recDeleteNode(key, subtree->getLeft()));
}
else if (subtree->getItem() < key)
{
subtree->setRight(recDeleteNode(key, subtree->getRight()));
}
else
{
if (subtree->getLeft() == nullptr && subtree->getRight() == nullptr)
{
delete subtree;
subtree = nullptr;
}
else if (subtree->getLeft() == nullptr)
{
BNode<ItemType> *temp = subtree;
subtree = subtree->getRight();
delete temp;
}
else if (subtree->getRight() == nullptr)
{
BNode<ItemType> *temp = subtree;
subtree = subtree->getRight();
delete temp;
}
else
{
BNode<ItemType> *temp = minValueNode(subtree->getRight());
temp->setLeft(subtree->getLeft());
temp = subtree;
subtree = subtree->getRight();
delete temp;
}
}
return subtree;
}
The ItemType comparison operators are overloaded, since the key is defined inside the actual data that the Node points to.
I've been staring at this for a while, but I cannot see what would cause it to pull out another nodes along with it.
else if (subtree->getRight() == nullptr)
{
BNode<ItemType> *temp = subtree;
subtree = subtree->getRight(); // this is nullptr
delete temp;
}
That getRight should be getLeft.
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'm having trouble implementing a binary search tree deletion algorithm on C++. If I try deleting the root, or direct children of the root, it works correctly. But it does not work for deeper levels (just outputs the same tree without any deletion). What is wrong with my code?
typedef struct Node {
int key;
Node *left = NULL;
Node *right = NULL;
} Node;
...
/*
* Delete <key> from BST rooted at <node> and return modified <node>.
*/
Node* BST::pop(Node *node, int key) {
// If <node> is a null pointer, return.
// If <node> doesn't contain the key, traverse down the tree.
// If <node> contains the key, perform deletion.
if (node == NULL) {
} else if (key < node->key) {
node->left = pop(node->left, key);
} else if (key > root->key) {
node->right = pop(node->right, key);
} else {
// If <node> is a leaf, just delete it
if (node->left == NULL && node->right == NULL) {
delete node; // deallocate memory (note: node still points to a memory address!)
node = NULL; // node points to null
}
// If <node> has a single child, replace <node> with its child
else if (node->left == NULL && node->right != NULL) {
Node *tmp = node;
node = node->right;
delete tmp;
tmp = NULL;
} else if (node->right == NULL && node->left != NULL) {
Node *tmp = node;
node = node->left;
delete tmp;
tmp = NULL;
} else {
node->key = findMax(node->left);
node->left = pop(node->left, node->key);
}
}
return node;
}
int BST::findMax(Node *root) {
if (root->left == NULL && root->right == NULL) {
return root->key;
} else {
int max = root->key;
if (root->left != NULL) {
int leftMax = findMax(root->left);
if (leftMax > max) {
max = leftMax;
}
}
if (root->right != NULL) {
int rightMax = findMax(root->right);
if (rightMax > max) {
max = rightMax;
}
}
return max;
}
}
A couple of things:
Second else if should be else if (key > node->key)
Your findMax function is exceptionally complex. The max in a BST from some root is really just traversing right children until there are no more right children (because anything in the left subtree must be less than the key you are currently evaluating, so leftMax can never be > max). Therefore it could be
int BST::findMax(Node *root) {
int max = root->key;
while (root->right != NULL) {
root = root->right;
max = root->key;
}
return max;
}
As long as the tree does not need to remain balanced, your general algorithm of just removing in case of a leaf, swapping the lone child if there is only one, and in the case of two children finding an inorder neighbor, swapping and deleting that node should be sound (not sure if you found this link, but: http://quiz.geeksforgeeks.org/binary-search-tree-set-2-delete/)
I coded case #2 in delete_node but am getting a segmentation error. I'm using the predecessor to delete the node. I'm not sure what happens when I am removing the root node with two children. Is it the same as deleting a non-root node with two children except you don't have to connect the predecessor's children? I think that's how I've coded it...
bool delete_node(Node*& root, KType key) {
// find target node to delete
Node* target = find(key, root);
if (!target) return false;
// find parent of target
Node* parent = find_parent(root, target);
// case 1: target is a leaf
if (target->left == NULL && target->right == NULL) {
// set parent's child pointer
if (parent != NULL) {
if ( parent->left == target )
parent->left = NULL;
else
parent->right = NULL;
}
else
root = NULL;
// free target
delete target;
return true;
}
// case 2: target has two children
else if (target->left != NULL && target->right != NULL) {
if (parent != NULL) {
//find predecessor
Node* temp = target->left;
while (temp != NULL) {
temp = temp->right;
}
// find predecessor's parent
Node* predecessorParent = find_parent(root, temp);
target->key = temp->key;
if (predecessorParent->left == temp) {
predecessorParent->left = temp->left;
} else {
predecessorParent->right = temp->left;
}
delete(temp);
return true; // return true when you're done.
} else {
//find predecessor
Node* temp = target->left;
while (temp != NULL) {
temp = temp->right;
}
target->key = temp->key;
delete(temp);
return true; // return true when you're done.
}
}
// case 3: target has only left child
else if (target->left != NULL) {
// set parent's child pointer
if (parent != NULL) {
if ( parent->left == target )
parent->left = target->left;
else
parent->right = target->left;
}
else
root = target->left;
// free target
delete target;
return true;
}
// case 4: target has only right child
else {
// set parent's child pointer
if (parent != NULL) {
if (parent->left == target)
parent->left = target->right;
else
parent->right = target->right;
}
else
root = target->right;
// free target
delete target;
return true;
}
return false;
}
find_parent function:
/**
* Finds the parent of node in the tree rooted at rootNode
*/
Node* find_parent(Node* rootNode, Node* node) {
if ( rootNode == NULL || rootNode == node ) {
return NULL;
}
else if ( rootNode->left == node || rootNode->right == node ) {
return rootNode;
}
else if (node->key < rootNode->key) {
return find_parent(rootNode->left, node);
}
else {
return find_parent(rootNode->right, node);
}
}
After a few questions and some nice answers and friendly helpers here. I got the sulotion to my porblem with the deleting in the binary tree, i got suggested that, i can not just delet the largest number in the tree cause its may not the last or it has childrens 1 ,2 or none, so i made the code down below, i used a lot commenting hope that can help you people help me. What i actually dont know now, is how do i call this RemoveLargest() function in my public and then later in main, even though i dont know if the code will run properly.
#include <iostream>
#include <string>
#include <cstdlib>
using namespace std;
template<class T>
class BinaryTree
{
struct Node
{
T data;
Node* lChildptr;
Node* rChildptr;
Node(T dataNew)
{
data = dataNew;
lChildptr = NULL;
rChildptr = NULL;
}
};
private:
Node* root;
void Insert(T newData, Node* &theRoot) //Insert elements into the tree start.
{
if(theRoot == NULL)
{
theRoot = new Node(newData);
return;
}
if(newData < theRoot->data)
Insert(newData, theRoot->lChildptr);
else
Insert(newData, theRoot->rChildptr);
} //end.
void PrintTree(Node* theRoot) //print me the tree /start
{
if(theRoot != NULL)
{
PrintTree(theRoot->lChildptr);
cout<< theRoot->data<<" \n";
PrintTree(theRoot->rChildptr);
}
} //end.
T Largest( Node* theRoot) // show me largest number /start.
{
if ( root == NULL )
{
cout<<"There is no tree";
return -1;
}
if (theRoot->rChildptr != NULL)
{
return Largest(theRoot->rChildptr);
}
T value = theRoot->data;
return value;
} //end.
void RemoveLargest(Node* theRoot) //remove the largest priority number from tree /start.
{
Node* current; //the current tree?
Node* parent; //the parent of the current node?
current=theRoot;
// 3 cases :
// 1. We're removing a leaf node
// 2. We're removing a node with a single child
// 3. we're removing a node with 2 children
//Node with single child.
if((current->lChildptr == NULL && current->rChildptr != NULL)||(current->lChildptr != NULL && current->rChildptr == NULL))
{
if(current->lChildptr == NULL && current->rChildptr != NULL)
{
if(parent->lChildptr==current)
{
parent->lChildptr = current->rChildptr;
delete current;
}
else
{
parent->rChildptr = current->rChildptr;
delete current;
}
}
else //left child ok, no right child
{
if(parent->lChildptr==current)
{
parent->lChildptr = current->lChildptr;
delete current;
}
else
{
parent->rChildptr = current->lChildptr;
delete current;
}
}
return;
}
//We found a leaf(a node with not a single child)
if(current->lChildptr == NULL && current->rChildptr == NULL)
{
if (parent->lChildptr == current)
parent->lChildptr = NULL;
else
parent->rChildptr = NULL;
delete current;
return;
}
//Node with 2 children
// replace node with smallest value in right subtree
if (current->lChildptr != NULL && current->rChildptr != NULL)
{
Node* checkr;
checkr = current->rChildptr;
if((checkr->lChildptr == NULL)&&(checkr->rChildptr == NULL))
{
current=checkr;
delete checkr;
current->rChildptr = NULL;
}
else //right child has children
{
//if the node's right child has a left child
//Move all the way down left to locate smallest element
if ((current->rChildptr)->lChildptr != NULL)
{
Node* lcurr;
Node* lcurrp;
lcurrp = current->rChildptr;
lcurr = (current->rChildptr)->lChildptr;
while(lcurr->lChildptr != NULL)
{
lcurrp = lcurr;
lcurr = lcurr->lChildptr;
}
current->data = lcurr->data;
delete lcurr;
lcurrp->lChildptr = NULL;
}
else
{
Node* temp;
temp = current->rChildptr;
current->data = temp ->data;
current->rChildptr = temp->rChildptr;
delete temp;
}
}
return;
}
};
public:
BinaryTree()
{
root = NULL;
}
void AddItem(T newData)
{
Insert(newData, root);
}
void PrintTree()
{
PrintTree(root);
}
T Largest()
{
return Largest(root);
}
void RemoveLargest()
{
RemoveLargest();
}
};
int main()
{
BinaryTree<int> *myBT = new BinaryTree<int>();
myBT->AddItem(5);
myBT->AddItem(1);
myBT->AddItem(4);
myBT->AddItem(2);
myBT->AddItem(3);
//for(int i = 0; i < 10; i++) //randommal tolti fel/fill with random
//myBT->AddItem(rand() % 100);
cout << "BinaryTree:" << endl; //kilistazaa a fat/ list my tree
myBT->PrintTree();
cout << "Largest element: " << myBT->Largest() << endl; //visszaadja a legnagyobb elemet/shows the largest number
myBT->RemoveLargest(); //suposed to delet the largest number
myBT->PrintTree(); //shows list again
}
edited the code, now its running, its creating the tree, shows the largest, but after i call my remove function still crashing... =/
Like I said before, I think you're making things overly complicated. You have to think of what it means that your node is the largest one, in the context of a binary search tree and the relationship between the keys in its nodes.
If a node is the largest one in the tree it cannot possibly have a right child pointer, because the right child would have to have a larger key. And then, if you know that it has at most a left child, you just replace your node with its possibly null left child, and you're done.
T ExtractLargest(Node*& n){
if (!n)
return -1;
if (n->rChildptr)
return ExtractLargest(n->rChildptr);
T result = n->data;
Node *d = n;
n = n->lChildptr;
delete d;
return result;
}