I have checked other similar topics, but none seemed to help me.
I am trying to write a destructor for this specific BST implementation. Every node contains a pointer to the parent, a pointer to the left node, a pointer to the right node and a pointer to the value it contains.
This is how the beginning of the class looks:
#ifndef BST_H
#define BST_H
#include <iostream>
template <typename T>
class BST{
private:
BST<T> *left;
BST<T> *right;
BST<T> *parent;
T *value;
public:
BST() {
this->parent = NULL;
this->left = NULL;
this->right = NULL;
this->value = NULL;
}
~BST() {
removeRecursively(this);
}
void removeRecursively(BST<T>* node) {
if (node->left != NULL)
removeRecursively(node->left);
if (node->right != NULL)
removeRecursively(node->right);
if (node->left == NULL && node->right == NULL) {
if (node->parent->left == node)
node->parent->left = NULL;
if (node->parent->right == node)
node->parent->right = NULL;
node->parent = NULL;
node->value = NULL;
delete node->value;
delete node;
}
}
void add(T value) {
if (this->value == NULL) { // root-only case
this->value = new T;
*(this->value) = value;
}
else {
if (value < *(this->value)) {
if (this->left != NULL) // has left child
this->left->add(value);
else { // has no left child
this->left = new BST<T>;
this->left->value = new T;
this->left->parent = this;
*(this->left->value) = value;
}
}
else {
if (this->right != NULL) // has right child
this->right->add(value);
else { // has no right child
this->right = new BST<T>;
this->right->value = new T;
this->right->parent = this;
*(this->right->value) = value;
}
}
}
}
void inOrderDisplay() {
if (this->left != NULL)
this->left->inOrderDisplay();
std::cout << *(this->value) << " ";
if (this->right != NULL)
this->right->inOrderDisplay();
}
BST<T>* search(T value) {
if (*(this->value) == value)
return this;
else if (this->left != NULL && value < *(this->value))
return this->left->search(value);
else if (this->right != NULL && value > *(this->value))
return this->right->search(value);
else
return NULL;
}
BST<T>* remove(T value) {
BST<T>* node = search(value);
if (node != NULL) {
if (node->left == NULL && node->right == NULL) { // leaf node
delete node->value;
if (node->parent->left == node) // is left child
node->parent->left = NULL;
else // is right child
node->parent->right = NULL;
delete node;
}
// 1 child nodes
if (node->left != NULL && node->right == NULL) { // has left child
if (node->parent->left == node) // is left child
node->parent->left = node->left;
else // is right child
node->parent->right = node->left;
delete node->value;
node->parent = NULL;
delete node;
}
if (node->left == NULL && node->right != NULL) { // has right child
if (node->parent->left == node) // is left child
node->parent->left = node->right;
else // is right child
node->parent->right = node->right;
delete node->value;
node->parent = NULL;
delete node;
}
// 2 children nodes
if (node->left != NULL && node->right != NULL) {
T aux;
BST<T>* auxNode = node->right;
while (auxNode->left != NULL)
auxNode = auxNode->left;
aux = *(auxNode->value);
if (auxNode->right != NULL) {
*(auxNode->value) = *(auxNode->right->value);
auxNode->right->parent = NULL;
auxNode->right->value = NULL;
auxNode->right = NULL;
delete auxNode->right;
}
else {
if (auxNode->parent->left == auxNode) // is left child
auxNode->parent->left = NULL;
else // is right child
auxNode->parent->right = NULL;
auxNode->value = NULL;
delete auxNode;
}
*(node->value) = aux;
}
}
return this;
}
};
#endif // BST_H
The BST class is used as below:
BST<int>* root = new BST<int>();
root->add(5);
root->add(2);
root->add(-17);
root->inOrderDisplay();
root->remove(5);
I mention that all the methods work properly (I decided not to post them, since they are not the subject of this question). The problem is that when I run my test file with Valgrind, it detects some memory leaks and I am sure that they occur because of the lack of a proper destructor (the above one produces a segmentation fault).
EDIT: I added the code for the other methods
Thank you!
Your basic design is kind of broken, at least IMO. That is, if you're willing to jump through enough hoops, you can probably make it work, but even at best it'll probably always be at least a little clumsy to work with.
I'd start by defining a separate class for the nodes in the tree. Then the tree itself will hold a pointer to the root of the tree and define most of the interface to the tree.
template <class T>
class Tree {
struct node {
node *parent;
node *left;
node *right;
T *value;
node(T *value)
: parent(nullptr), left(nullptr), right(nullptr), value(new T(value))
{
}
~node() {
delete(left);
delete(right);
delete(value);
}
} *root;
Tree() : root(nullptr) {}
~Tree() {
delete(root);
}
};
Destruction doesn't have to be explicitly recursive. The delete(left) (for example) will invoke the dtor for the left child node (if there is one) and that'll invoke the dtor for its child nodes, and so on. When we reach a leaf node, we'll end up with (the equivalent of) delete nullptr;, which is defined as doing nothing, stopping the recursion.
You should swap the lines:
node->value = NULL;
delete node->value;
Like this:
delete node->value;
node->value = NULL;
if you first assign NULL you delete nothing.
The hardest bugs to find are the ones in the code you've decided not to look at because you've convinced yourself they're not relevant.
Anyways, there is one evident problem with your implementation: after removing a bunch of other stuff, you are basically doing
class foo
{
~foo() { delete this; }
};
Use RAII with unique_ptr to avoid those issues:
template <typename T>
class BST{
public:
BST() = default;
~BST() = default;
BST(const BST&) = delete;
BST& operator =(const BST&) = delete;
BST(BST&&) = delete;
BST& operator =(BST&&) = delete;
private:
std::unique_ptr<BST<T>> left;
std::unique_ptr<BST<T>> right;
BST<T>* parent = nullptr;
std::unique_ptr<T> value;
};
Related
I'm building a function for deleting a node in a binary tree; since two of three cases are fairly simple (no children and 1 child) I've built another function to "re-arrange" the nodes beneath the one I'm trying to delete, in case it has two children.
I built the function so that it returns a pointer, which is the re-arranged subtree, but I've also tried making a function that just changes the tree itself, without returning a value, but both seem to create a loop where the tree changes: when I try to print the tree, it's stuck printing between the value I replace and it's left child, back and forth until eventually a segfault 11 pops up. I'm really lost as even the debugger isn't helping me understand where the issue is, can anyone tell me where to look? Thank you in advance for your kind attention and your time
Here is the tree I was thinking of before the deletion:1
And here it is after the deletion:2
'''
if (found the value) // value is head (recursive)
{
Node* to_delete = head;
...
head = subNode (head);
delete to_delete;
}
'''
Node* b_tree :: subNode (Node*&head)
{
Node*curr = head->right;
if (((!curr->right) && (!curr->left)) || (!curr->left)) // if it's a leaf or at least if the left pointer is NULL
{
curr->left = head->left;
head->left = NULL;
Node*to_return = curr;
return to_return;
}else // if there's a lower value...
{
while (curr->left != NULL) // find the lowest in the sub-tree
{
curr = curr->left;
}
if (!curr->right)
{
curr->right = head->right;
curr->left = head->left;
Node* to_return = curr;
curr = NULL;
return to_return;
} else {
Node* temp = curr->right;
curr->right = head->right;
curr->left = head->left;
Node*to_return = curr;
curr = temp;
return to_return;
}
}
};
''' Here's the whole code
'''
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;
struct Node
{
Node ();
Node (string, int);
string name;
int value;
Node* left;
Node* right;
};
Node :: Node ()
{
left = right = NULL;
}
Node :: Node (string nome, int valore)
{
this->name = nome;
this->value = valore;
this->left = NULL;
this->right = NULL;
};
class b_tree
{
private:
Node* head;
void insRic (Node*&, Node);
void stampaRic (Node*);
void deleteRic (Node*&, Node);
Node* subNode (Node*&);
public:
b_tree ();
void inserimento (Node);
void delete (Node);
void stampa ();
};
b_tree :: b_tree ()
{
this->head = NULL;
};
void b_tree :: inserimento (Node temp)
{
insRic(this->head, temp);
};
void b_tree :: insRic (Node*& head, Node temp)
{
if (head != NULL)
{
if (temp.value < head->value)
{
insRic(head->left, temp);
} else if (temp.value > head->value)
{
insRic(head->right, temp);
}
} else
{
head = new Node (temp.name, temp.value);
}
};
void b_tree :: stampa ()
{
stampaRic (this->head);
};
void b_tree :: stampaRic (Node* head)
{
if (head != NULL)
{
if (head->left != NULL)
{
stampaRic (head->left);
}
cout << "Nome: " << head->name << " | Valore: " << head->value << endl;
if (head->right != NULL)
{
stampaRic (head->right);
}
}
};
Node* b_tree :: subNode (Node*&head)
{
Node*curr = head->right;
if (((!curr->right) && (!curr->left)) || (!curr->left)) // if it's a leaf or at least if the leftious pointer is NULL
{
curr->left = head->left;
head->left = NULL;
Node*to_return = curr;
return to_return;
}else // if there's a lower value...
{
while (curr->left != NULL) // find the lowest in the sub-tree
{
curr = curr->left;
}
if (!curr->right)
{
curr->right = head->right;
curr->left = head->left;
Node* to_return = curr;
curr = NULL;
return to_return;
} else {
Node* temp = curr->right;
curr->right = head->right;
curr->left = head->left;
Node*to_return = curr;
curr = temp;
return to_return;
}
}
};
void b_tree :: delete (Node temp)
{
deleteRic (this->head, temp);
};
void b_tree :: deleteRic (Node*& head, Node temp)
{
if (head != NULL) // if head points ot something
{
if (head->value != temp.value) // if the node I'm trying to delete has a different name, jusr call it again until it finds it
{
if (temp.value < head->value)
{
eliminaRic (head->left, temp);
} else if (temp.value > head->value)
{
eliminaRic (head->right, temp);
}
} else // once I've found the value I'm trying to delete;
{
Node*to_delete = head;
if ((head->right == NULL) && (head->left == NULL)) // checking that the node is not a leaf
{
head = NULL;
delete to_delete;
} else if (head->left == NULL) // if leftious pointer is null
{
to_delete = head->right;
delete to_delete;
} else if (head->right == NULL) // if right pointer in null
{
head = head->left;
delete to_delete;
}
else if ((head->right != NULL) && (head->left != NULL)) // if neither pointer is null (2 children);
{
head = subNode (head);
delete to_delete;
}
}
}
}
int main ()
{
b_tree*albero = new b_tree ();
Node temp = Node ();
for (int i = 0; i < 7; i++)
{
cout << "Insert name and value; name: "; cin >> temp.name; cout << " value : "; cin >> temp.value; cout << endl;
albero->inserimento(temp);
}
albero->stampa();
cout << "Insert the value of the node you wish to delete: "; cin >> temp.value;
albero->delete(temp);
albero->stampa();
}
'''
Here are some issues:
The main problem is in the second half of subNode. The following line (that occurs twice) creates a cycle:
curr->next = head->next;
As curr is a descendant of head->next, this closes a cycle. This cycle is not broken by anything that follows.
curr = NULL or curr = temp is useless. It only sets a value of a variable and doesn't mutate the tree (which may be what you thought it did).
to_return should not be set to curr, but to head->next.
Not a problem, but the if condition at the top of subNode is equivalent to just !curr->prev.
Not a problem, but head->prev = NULL is not really needed, as head is going to be deleted anyway.
When these points are taken into account, some code repetition can be avoided, and the code can be reduced to the following:
Node* b_tree :: subNode (Node*&head)
{
Node* curr = head->next;
while (curr->prev != NULL)
{
curr = curr->prev;
}
curr->prev = head->prev;
return head-next;
};
Side note: this algorithm will more quickly increase the gravity with which the tree is unbalanced, as a whole subtree is moved at a lower place in the tree. This may even double the height of the tree with just one delete operation. I would therefor prefer the original, popular algorithm for deletion.
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.
This is a code to delete a node from a Binary Search Tree:
My question is: Why do we pass the node pointer by reference to DelSingle function but we only pass a node pointer to DelDoubleByCopying function?
template <class T>
bool BST<T>::DeleteNode(T& val)
{
BSTNode<T> * node = root, *prev = NULL;
if (IsEmpty() == true)
return false;
while (node != NULL)
{
if (node->val == val)
break;
prev = node;
if (val < node->val)
node = node->left;
else
node = node->right;
}
if (node == NULL)
return false;
if (node->left == NULL || node->right == NULL)
{
if (node == root)
DelSingle(root);
else if(node == prev->left)
DelSingle(prev->left);
else
DelSingle(prev->right);
}
else
DelDoubleByCopying(node);
return true;
}
template <class T>
void BST<T>::DelSingle(BSTNode<T>*& ptr)
{
BSTNode<T>* delNode = ptr;
if(delNode->left == NULL) // node does not have a left child
ptr = delNode->right;
else if(delNode->right == NULL) // node does not have a right child
ptr = delNode->left;
delete delNode;
}
template <class T>
void BST<T>::DelDoubleByCopying(BSTNode<T>* node)
{
BSTNode<T> *prev, *rep;
rep = node->left; //Find the largest child in the left subtree
prev = node;
while (rep->right != NULL)
{
prev = rep;
rep = rep->right;
}
node->val = rep->val;
if (prev == node)
prev->left = rep->left;
else
prev->right = rep->left;
delete rep;
}
And this is the class of Binary Search Tree node:
template <class T>
class BSTNode
{
public:
BSTNode(T& val, BSTNode* left, BSTNode* right);
~BSTNode();
T GetVal();
BSTNode* GetLeft();
BSTNode* GetRight();
private:
T val;
BSTNode* left;
BSTNode* right;
int depth, height;
friend class BST<T>;
};
DelSingle()
Given the follwing structure
parent
ptr1 ptr2
child1
And sssuming we are deleting ptr1:
Basically, what DelSingle() does is to swap child1 with ptr1 and then get ride of child1 (child1 is not what ptr1 once was).
ptr is passed by reference because you are actually changing the pointer, parent's left child is not child1.
DelDoubleByCopying()
You don't need to pass node by reference because node is not going to change, the one who changes is node->left (or node->right).
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;
}
I'm trying to make a BST and need to print it inorder, postorder, and preorder.
The thing am not sure about is how to create this tree in my main() function.
struct Tree_Node
{
Tree_Node *right;
Tree_Node *left;
int info;
};
class bTree
{
private:
Tree_Node *root;
public:
bTree();
void bTree::Insert(Tree_Node*& tree, int item);
void bTree::preorderPrint(Tree_Node *root);
};
bTree::bTree()
{
root = NULL;
}
void bTree::Insert(Tree_Node*& tree, int item)
{
if (tree == NULL)
{
tree = new Tree_Node;
tree->right = NULL;
tree->left = NULL;
tree->info = item;
}
else if (item < tree->info)
Insert(tree->left, item);
else
Insert(tree->right, item);
}
void bTree::preorderPrint(Tree_Node *root)
{
if ( root != NULL )
{
cout << root->info << " ";
preorderPrint( root->left );
preorderPrint( root->right );
}
}
void main()
{
// This is where I need help at
// I'm not sure how to insert a new node
bTree Test;
Test.Insert(
}
By the looks of things, you can just write
Test.Insert(Test.root, 3); // Insert 3
Test.Insert(Test.root, 4); // Insert 4
and that should work. Of course, you'll have to make root public.
However, this is a bit awkward, since the first parameter will always be bTree.root - and you don't need to make that public. Remember that the user of your data type (you or anyone else) shouldn't have to care about internals such as nodes - they only care about their data. Instead, I'd recommend making a convenience Insert method which only needs to take an integer (not a tree node) - this is called Overloading.
void bTree::Insert(int item)
{
Insert(root, item);
}
// Keep the other insert method, but make it private.
Then you can just write:
Test.Insert(3);
Test.Insert(4);
void bTree::Insert(int item)
{
Tree_Node * node = new Tree_Node;
node->left = NULL;
node->right = NULL;
node->info = item;
if (root == NULL)
{
root = node;
return;
}
Tree_Node * t = root;
Tree_Node * p = root;
while(1)
{
if (item < t->info)
{
t = t->left;
if(t == NULL)
{
p->left = node;
return;
}
}
else if(item > t->info)
{
t = t->right;
if(t == NULL)
{
p->right = node;
return;
}
}
else //item already exists in the tree
return;
p = t;
}
}
//now you can insert nodes like
Test.Insert(5);
Test.Insert(6);