Removing from a Binary Search Tree - c++

I am trying to write a remove from a binary tree function. I'm kinda lost so I'm trying to handle it case by case, starting with if the value I'm trying to remove is in the root of the BST. To test my function, I am first calling a printcontents() function that prints all the contents of the tree, then I'm calling remove(8) [8 being the value in my root at the moment), and then calling printcontents() again. The way I'm doing it is by trying to replace the root with the "right-most" value in the left side of the tree. When I call printcontents the second time, it prints the new root value correctly, but when it continues printing the contents and reaches the point where that value used to be, it has a random long number "-572......"(although i don't think the number matters) and then my program crashes. I see my root's value is being replaced, but what happens afterwards??
Here's my remove function:
void BinarySearchTree::remove(int value) {
Node* tmp = head;
Node* tmp2 = head;
if (head->data == value && head->left != NULL) {
tmp=tmp->left;
while (tmp->right != NULL) {
tmp=tmp->right;
}
while (tmp2->right->right != NULL) {
tmp2=tmp2->right;
}
if (tmp->left == NULL) {
head->data = tmp->data;
tmp2->right = NULL;
delete tmp;
}
if (tmp->left != NULL) {
head->data = tmp->data;
tmp2->right = tmp->left;
delete tmp;
}
}
It's obviously incomplete, but I'm testing it to only handle the case in which the root is removed and replaced by the right-most value in the left side of the tree (assuming there is a left side, which there is), and I feel like logically it should be working, so perhaps it is when I "delete tmp" that things go wrong. I don't know whether posting my whole program will be necessary, but if so, let me know!

May I suggest that instead of writing out for root, why don't you treat the case as it is dealt with in CLRS : That is two distinct cases.
1. When node to be deleted is a leaf
2. When node to be deleted is non-leaf(in that case replace it with inorder successor/predecessor).
The root deletion obviously falls under the second case. This is just a suggestion.

Related

How to parse a large tree?

recently I passed a programming interview where I had to create a method that returns the address of a node (belonging to a tree). The method takes an integer value as an argument.
My code worked on a small tree, but when searching a large tree (300,000 nodes) I got an error stating "cannot access address '0x.....'".
What should I do to fix this?
'''
struct Node
{
int value;
Node* left = nullptr;
Node* right = nullptr;
Node* find_node(int);
};
Node* Node::find_node(int v)// The function is working on small trees only
{
if(this->value == v) //comparing the the value inside the root with the function's argument
return this;
else if(this->value > v) //if v is smaller than the node's value, search the next left node
{
if(this->left == nullptr) //checking if the next node on the left exists
return nullptr; //null returned if there is no more nodes
else
return (this->left)->find_node(v); //Call the find_node function recursively on the left node
}
else if(this->value < v) //if v is bigger than the node's value, search the next right node
{
if(this->right == nullptr) //checking if the next node on the left exists
return nullptr; //null returned if there is no more nodes
else
return (this->right)->find_node(v);// Call the find_node function recursively on the right node
}
return nullptr;// If the value is not found
}
'''
Your code needs lots of activation records on the call stack for repetitive calls to find_node(v). And it may lead to overflow of the call stack.
To avoid it, you can use non-recursive versions of binary search that uses a loop instead. For more information, check this link.

Pointer to a new node in a Binary Tree

I was solving problem of insertion of node in a binary tree. I have the following doubts:
1) If we are inserting a node then we should return a pointer pointing to that node as then only we will be able to access the node, right?
2) Then here why are we returning root? We must return root->left or root->right accordingly, where am I wrong?
struct node* insert(struct node* root, int data)
{
if (root == NULL) //If the tree is empty, return a new,single node
return newNode(data);
else
{
//Otherwise, recur down the tree
if (data <= root->data)
root->left = insert(root->left, data);
else
root->right = insert(root->right, data);
return root;
}
}
3) Is this root which the above code returns the changed one from what it was previously due to recursion?
You misunderstand the return value.
The return value of this insert function is a pointer to the subtree that now has data inserted into it. If the passed in root was null, this is a new 1 node tree; if the passed in root is non-null, the return value is the same root.
This makes the recursion a bit simpler. We simply recurse until we run head-on into nullptr in a branch. Then the recursion stops, and the return value sets the parent's left or right node.
To create a brand new tree you type:
node* new_tree = insert(nullptr, 7);
to insert something into an existing tree you type:
existing_tree = insert(existing_tree, 7);
or equivalently
insert(existing_tree, 7);
so long as existing_tree isn't null.
This "double use" of the function (to both create and modify a tree) can confuse, but it makes the specific recursive use a tad less verbose, and makes the "empty tree is a nullptr" and "always do existing_tree = insert(existing_tree, val);" is a rule that makes the empty tree as the null tree work.
This is, however, a very C way of doing things.
A more c++ way of doing things would be:
std::unique_ptr<node> insert(std::unique_ptr<node> root, int data)
{
if (root == nullptr) //If the tree is empty, return a new,single node
return std::make_unique<node>(data);
else
{
//Otherwise, recur down the tree
if (data <= root->data)
root->left = insert(std::move(root->left), data);
else
root->right = insert(std::move(root->right), data);
return std::move(root);
}
}
where the flow of data into and out of the function is more explicit, and we assume node has a constructor that takes data.
This recursive insert should always return the very root node of the tree. Just because you read return root doesn't mean the original function call has finished executing, it just means the n'th recursion has finished. The recursive calls have all been pushed onto the stack and therefore must all be resolved before the original caller receives the returned value.
You can get back to the inserted node by doing a find for the inserted value.

How can I delete a node in my linked list in C++?

I've pasted my work so far here:
http://codepad.org/WhJuujRm
The concepts of linked lists boggle my mind, so I thought I'd practice. I know how to add nodes, and edit nodes, but I don't know how to remove nodes in my particular scenario.
My Pseudo Code:
previous == now - 1;
if(stdid == now->getID());
previous->setNext(now->getNext);
delete now;
return;
How could I implement this?
The mind-tease in deleting an element from a linked list is updating the pointer that brought you to the element in the first place. In your list case, that could be top (and/or possibly bottom), it could be some node's next. As you walk through the list hunting with a cur pointer, keep a prev pointer which you advance one step behind as you enumerate. Assuming you find the victim node (if you don't, there's nothing to do, woot!), prev will be in one of two states:
It will be NULL, in which case top is the pointer that refers to your victim node and top must be updated, or...
It will be some pointer to a node, in which case that node's next member needs to be updated to the reflect the victim node's next member value.
In both cases bottom may need updating as well. In the first case bottom will need to change if the list only had one node and you're deleting it. i.e. you will have an empty list when finished. Easy enough to tell, since top will be NULL after to detach cur and set top equal to cur->next. Even easier for you, since you're keeping a size member in your list container; if it was 1, you know both head and bottom
In the second case, the last node may be the victim node. In that case bottom has to be updated to reflect the new end of the list (which is coincidentally in prev, and may be NULL if, once again, the list had only a single element. How do you tell if the victim was the last node in the list? If it's next member is NULL, it has to be the last node, and bottom must be updated.
So something like this, a delete function based on ID search
void deleteStudent(int id)
{
student *cur = top, *prev = nullptr;
while (cur && cur->getID() != id)
{
prev = cur;
cur = cur->getNext();
}
// found a node?
if (cur)
{
student *pNext = cur->getNext();
// set new next pointer for prev, or new top
if (prev)
prev->setNext(pNext);
else
top = pNext;
// update bottom if needed
if (!pNext)
bottom = prev;
delete cur;
--scnt;
}
}
Other delete options and criteria I leave to you.
Best of luck.
This should work, but I have not tested it.
There is a special case, when the first node is deleted. previous is set to NULL for the first iteration, and the top has to be adjusted in this case.
I didn't use bottom, because it's not the way I would do it. If you use bottom, there is a second special case, when you delete the last student. I would mark the end of the list with a next pointer set to NULL, because this eliminates this special case.
bool deleteStudent(int id)
{
student* now = top;
student* prev = NULL;
while(now != NULL) {
student* next = now->getNext();
if(id == now->getID()) {
delete now;
if(prev) prev->setNext(next);
else top = next;
return true;
}
prev = now;
now = next;
}
return false;
}
I did not use your notation but I think you can get the point.
prev = NULL;
current = top;
while (current != NULL && !isfound(current)){
prev = current;
current = current->next;
}
// current point to the element you want to delete (if not NULL)
if(current != NULL) {
if(previous != NULL) {
previous->next = current->next;
}
else {
top = current->next;
}
delete current;
}

Nodes in binary tree are null

void MultiMap::insert(string key, unsigned int value)
{
if(head == nullptr)
head = new Node(key, value);
else
{
Node* tempNode = head;
while(tempNode != nullptr)
{
if(key <= tempNode->m_key)
tempNode = tempNode->m_left;
else if(key > tempNode->m_key)
tempNode = tempNode->m_right;
}
/*line 1*/tempNode = new Node(key, value);
//*line 2*/head->m_left = new Node(key, value);
}
}
For an assignment, I have to make a binary tree class, "MultiMap" with nodes that contain a string and an int.
The above is code to insert a new node into the tree. The nodes are sorted by their strings. If the string of the node I am trying to insert is > the current node, the program should try to insert it on the right branch of the tree, and if it is <=, the program should try to insert it on the left branch of the tree.
I tested it by trying to insert two nodes: (Joe, 5) and (Bill, 1) in that order, so if the program works properly, "bill" should be on the left branch of "joe".
Line 2 is commented out.
If I use line 1, the program compiles and "inserts" the second node, but when I try to look for it with other code, it only finds a nullptr. If I replace line 1 with line 2, the program works as expected.
"tempNode" is what I'm using to trace through the tree to find the appropriate place to insert a new node. "head" is a pointer to the first node in the tree. "m_left" and "m_right" are pointers to nodes, representing the left and right branches of a node, respectively.
I don't know why the two lines don't do the same thing even though at that point, it seems like tempNode and head->m_left are pointing to the same location in memory: the left branch of the first node.
Pointers are variables that hold addresses. There is nothing magic about them. Line 1 does this:
tempNode = new Node(key, value);
This doesn't insert anything into your tree. In fact, it just leaks memory.
What tempNode pointed to prior to this statement is irrelevant. More importantly, how tempNode held that prior value is already lost because you're already descended down the tree one level. Two pointers holding the same address just means the address is reachable with two pointers. Assigning a new address to a pointer has no effect on the previously addressed entity (if there was any).
Your task should be finding the pointer that should be filled in with the address of a newly allocated object. You found it (sort of). Unfortunately you also lost it as soon as you walked into it with your step "down" the tree for the final null-detection. As soon as this:
while (tempNode != nullptr)
becomes false and breaks, you're already one node too far. There are a number of ways to handle this. Some people like using a "parent" pointer, but that just means you have to special-case an empty map condition. Consider this instead:
void MultiMap::insert(string key, unsigned int value)
{
// pp will always point to the pointer we're testing
// i.e. a pointer to pointer.
Node **pp = &head;
while (*pp) // while whatever pp points to is a non-null-pointer
{
if (key < (*pp)->m_key)
pp = &(*pp)->m_left; // put address of left-pointer into pp
else if ((*pp)->m_key < key)
pp = &(*pp)->m_right; // put address of right pointer into pp
else break; // strict weak order match
}
if (*pp)
{
// found matching key. NOTE: unclear if you wanted to just update or not
}
else
{
// allocate new node.
*pp = new Node(key,value);
}
}
And you'll notice other than initializing our pointer-to-pointer with the address of the head node pointer, head is never referenced again.
Finally, notice there is no special-case head-node test. If the map is empty and the head pointer is NULL, this will automatically create a new node and make it the root.
What is going on here:
Node* tempNode = head;
while(tempNode != nullptr)
{
if(key <= tempNode->m_key)
tempNode = tempNode->m_left;
else if(key > tempNode->m_key)
tempNode = tempNode->m_right;
}
OK, now tempNode == nullptr, and it does not point to any node of the tree. As it is the variable on the stack, the next line:
/*line 1*/tempNode = new Node(key, value);
just initializes this local pointer and does not affect the tree itself. (Really here will be a memory leak.)
In your second line you initialize the node in the tree:
head->m_left = new Node(key, value);
But only for head->m_left.
So you can write:
if (key <= tempNode->m_key) {
if (tempNode->m_left == nullptr) {
tempNode->m_left = new Node(key, value);
break; // traverse tree loop
} else {
tempNode = tempNode->m_left;
}
}

Tree delete node

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 making 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;
}
That is some terrible pseudocode, and at a glance it didn't even look right (if this is a binary search tree, as BST would indicate, then the circled part is wrong). There is much better information about binary search trees available on the internet.
At any rate, you're trying to find the smallest element in the right subtree, since that will be less than all the other elements in the right subtree but greater than all the other elements in the left subtree.
Looks like your minimum function is right. You need to delete the reference to minNode after you free it. So (minNode->parent)->left = NULL or something slightly more tedious if you don't have a parent pointer. Right now that left just points to an empty space in memory, leading to completely random behavior.