void MorrisTraversal(tNode* root)
{
tNode *current, *pre;
if (root == NULL)
return;
current = root;
while (current != NULL)
if (current->left == NULL) {
current = current->right;
}
else {
pre = current->left;
while (pre->right != NULL
&& pre->right != current)
pre = pre->right;
if (pre->right == NULL) {
pre->right = current;
current = current->left;
}
else {
pre->right = NULL;
current = current->right;
}
}
}
}
I use this function to travers the bst, i also have a function (delete) that takes a node as parameter and delets it from the tree.
I'd like for example to remove from the tree all nodes whose value is 4, is it possible to do so using the functions mentioned above?
Like traversing the tree one time and removing all the nodes with a certain property.
I hope my problem it's clear...
Thanks in advance!
It is possible, but you will need to modify your Traversal function.
Currently MorrisTraversal traverses the tree, but it doesn't do anything to any node. You could modify MorrisTraversal to call a function on the current node (probably on the line after while (current != NULL).
I would first try putting a simple print function/line there that prints the value of the current node and run it to verify your tree traversal works as expected.
Then you could change the print function to modify the node in some way.
If you do delete the node, you will have to be careful to clean up the tree. Eg. if you have the following tree Where A is the root node:
A-B-C
|
D-E
|
F
What happens if you delete B? Will you move C, A. or D into B's place?
Good luck!
Related
I was trying to perform deletion in a binary search tree (BST), but it was giving a segmentation fault after printing the deleted array.
treeNode *minVal(treeNode *root)
{
if(root == NULL || root->left==NULL)
return root;
else
minVal(root->left);
}
treeNode *deleteBST(treeNode *root,int value)
{
treeNode *tempNode;
if(value>root->value)
deleteBST(root->right,value);
else if(value<root->value)
deleteBST(root->left,value);
else
{
if(root->left==NULL)
{
tempNode = root->right;
free(root);
return tempNode;
}
else if(root->right==NULL)
{
tempNode = root->left;
free(root);
return tempNode;
}
else
{
tempNode = minVal(root->right);
root->value=tempNode->value;
root->right = deleteBST(root->right,tempNode->value);
}
}
return root;
}
Presumably the point of deleteBST() returning a treeNode * is that the return value points to the new root of the (sub)tree. The new root will differ from the original one if the deleted value was found in the original root, so some means of updating the root is required.
That needs to be handled appropriately by this function's callers, including this function itself when it performs recursive calls. Your function fails to do this.
Your function also fails to properly handle the case when the specified value is not found in the tree. It will eventually attempt to dereference a null pointer if such a value is passed.
And the actual deletion performed by your function is totally inadequate. It frees the node where the value is found, but it does not adjust the subtree rooted at that node to have a valid structure, and it leaves the parent of the deleted node with a (then) invalid child pointer.
The general scheme for deletion from a BST would be like this:
if the root, R, of the tree is null then return null; otherwise,
if the value to delete is less than the value of R, then
delete it from the left subtree and update R's left pointer to point to the new root of the left subtree; otherwise,
if the value to delete is greater than the value of R, then
delete it from the right subtree and update R's right pointer to point to the new root of the right subtree; otherwise,
the value to delete is the value of R itself. In this case,
Select a new root for this subtree:
if both R's children are null then the new root is also null.
if exactly one child is null then the other is the new root.
if neither child is null then you can choose either the smallest node in the right subtree or the largest one in the left subtree. These are R's immediate in-order successor and predecessor, respectively.
In the case where neither of R's children is null, update the subtree appropriately. Supposing that the smallest node of the right subtree, MR, is the selected new root, you must
remove MR from its current parent by updating the parent's pointer to it to point instead at MR's right child (which might or might not be null). Including when the parent is R.
update MR's left and right child pointers to be the same as R's.
Free R.
return the root of the subtree (original or new, as appropriate)
updated working code is attached with comments to the errors.
treeNode *minVal(treeNode *root)
{
if(root == NULL || root->left==NULL)
return root;
else
return minVal(root->left);//1.returning a value is a must here
}
treeNode *deleteBST(treeNode *root,int value)
{
treeNode *tempNode;
if(root==NULL)//2.handling the base case
return NULL;
if(value>root->value)
return deleteBST(root->right,value);
else if(value<root->value)
return deleteBST(root->left,value);
else
{
if(root->left==NULL)
{
tempNode = root->right;
free(root);
return tempNode;
}
else if(root->right==NULL)
{
tempNode = root->left;
free(root);
return tempNode;
}
else
{
tempNode = minVal(root->right);
root->value=tempNode->value;
root->right = deleteBST(root->right,tempNode->value);
return root;//can be skipped
}
}
return root;
}
I'm using Morris inorder traversal to traverse (iterate over all nodes of) a binary tree. The relevant code (inspired by the C code here) looks like this:
#include <stdio.h> /* printf(). */
#include <stdlib.h> /* NULL. */
struct node {
struct node *left, *right;
int data;
};
void traverse(struct node *root) {
struct node *current = root;
struct node *pre;
while (current != NULL) {
if (current->left == NULL) goto process_node;
/* Find the inorder predecessor of current */
pre = current->left;
while (pre->right != NULL && pre->right != current) {
pre = pre->right;
}
if (pre->right == NULL) {
/* Make current the right child of its inorder predecessor */
pre->right = current; /* This breaks the tree temporarily. */
current = current->left;
} else {
/* Revert the changes made in the 'if' part to restore the
* original tree i.e., fix the right child of predecessor.
*/
pre->right = NULL;
process_node:
printf("%d ", current->data);
if (current->data < 0) {
/* !!! Add cleanup code here. */
return; /* Stop the traversal. */
}
current = current->right;
}
}
}
This code compiles and works correctly if there are no nodes with negative values. For nodes with negative values, I want to stop the traversal right after the first negative value. However, at that point some of the ->right pointers are broken (see the code line with this breaks in it), and I need to add cleanup code (to the code line with !!! in it) to restore these broken pointers, and make the tree valid again.
My question: What should this cleanup code be?
Please note that I still want to keep the Morris inorder traversal algoritm, because the program runs in a resource-constrained system with a small fixed stack and no dynamic memory available. So replacing the Morris algorithm with another traversal algorithm which doesn't need cleanup, but it uses more memory (e.g. recursive traversal or a traversal with a manually managed path stack) is not a valid answer to my question.
There's likely a better solution to this, but here's something I hacked together. It works by having a new exit variable, which gets flagged when a negative value is read. It then continues running as usual until after the functions's already existing code to revert the changes is ran, at which point it will early exit.
The one flaw of this implementation is that the program will still attempt to process another node before this happens, if the negative value node is not a leaf. Wrapping most of the process_node label's code in an if (!exit) so that it doesn't get processed when it's trying to exit fixes this.
void traverse(struct node *root) {
struct node *current = root;
struct node *pre;
int exit = 0;
while (current != NULL) {
if (current->left == NULL) goto process_node;
/* Find the inorder predecessor of current */
pre = current->left;
while (pre->right != NULL && pre->right != current) {
pre = pre->right;
}
if (pre->right == NULL) {
/* Make current the right child of its inorder predecessor */
pre->right = current; /* This breaks the tree temporarily. */
current = current->left;
} else {
/* Revert the changes made in the 'if' part to restore the
* original tree i.e., fix the right child of predecessor.
*/
pre->right = NULL;
if (exit) return;
process_node:
if (!exit){
printf("%d ", current->data);
if (current->data < 0) {
/* !!! Add cleanup code here. */
exit = 1;
}
}
current = current->right;
}
}
}
I am trying to delete the whole binary search tree ( every node in the tree), which one of these functions do you think will work better?
private:
struct Node {
string value;
Node* left;
Node* right;
};
Node* root;
public:
BST() {
root = NULL;
}
~BST() {
delete root->left;
delete root->right;
}
or:
...
void destroyTree (Node*& tree) {
while (tree != NULL) {
tree = tree->left;
delete tree;
}
while (tree != NULL) {
tree = tree->right;
delete tree;
}
delete tree;
}
Neither the proposed destructor ~BST() nor the proposed function destroyTree()of the class BST will delete the BST instance and will be better than the other.
Explanation 1 - what the destructor ~BST() is doing ?
The proposed source is simple delete root->left; followed by delete
root->right; and will delete only 2 nodes of the BinarySearchTree.
The node root is not check with NULL and otherwise is never deleted,
The node root->left is not check with NULL too,
The node root->right is not check with NULL too,
Explanation 2 - what the function destroyTree() is doing ?
Two while-loops to try the exploration of the tree, one for the left
branch and one for the right branch. The condition (tree != NULL) is
present only to exit from the loop but doesn't check if the current
node could be deleted.
With the left loop, only ->left nodes of the root->left will be deleted until tree->left == NULL and CRASH when trying to delete tree;,
Even after adding a if (tree==NULL) break; to exit from the loop before calling the delete tree; a new CRASH in the right loop,
And the last delete tree; is a non-sense because at this position, tree is always '== NULL`.
Bonus Solution - based to a modification of the function destroyTree().
A recursive function will be the simplest way to delete all created
and inserted nodes. But be aware with the size of the BinarySearchTree
and especially the deepest branch.
void destroyTree(Node *cur_node) {
if (cur_node!=NULL) {
// explore and delete on left
destroyTree(cur_node->left); // recursive-call
// explore and delete on right
destroyTree(cur_node->right); // recursive-call
// delete each node
delete cur_node;
}
}
I'm trying to create an AVL Tree iterator, but I'm having trouble doing so. This is the code I have to get the first node, which successfully returns the minimum.
AVLPtr node = iter->list->root;
AVLPtr current = iter->current;
AVLPtr last = iter->last;
AVLPtr parent;
if(current == NULL || current->parent == NULL)
parent = NULL;
else
parent = iter->current->parent;
if(last == NULL && current == NULL){
while(node->leftChild != NULL){
node = node->leftChild;
iter->current = node;
}
}
I'm getting a SegFault when I go to get the next node. I think this is because I am actually changing the parent of the node to NULL in my first if statement. I then mess up my list by eventually making the root the minimum in the while loop. My question is how do I get this first node without changing the parent or root nodes? Or is there something else that I am missing?
EDIT: Should I extract the object that each node in the tree holds into a separate linked list by using a recursive inorder call?
I do not know what do you mean by first element, but I will assume that you mean the leftmost leaf. If so, then you can find it this way:
AVLPtr first = iter->list->root;
AVLPtr last = iter->list->root;
while(first->leftChild != NULL){
first = first->leftChild;
}
iter->current = first;
You should always change iter->current and use its links to left, right and parent.
How can I free only a single node in a linked list? The following frees the whole linked list but I wanted to free only one node in the linked list.
//Here's my code for delete
while(headPtr!=NULL)
{
temp = headPtr;
headPtr = headPtr->next;
if(strcmp(temp->fname, stdfname) ==0 &&
strcmp(temp->sname, stdsname) ==0 )
{
free(temp);
}
}
You first need to know the previous node. Because of that, you need to iterate until you hit the node you want to delete. In that process you need to remember the previous node. Then you need to connection the previous and next nodes, thus "delinking" the node you want to delete.
currentNode = headNode;
previousNode = NULL;
while (currentNode != NULL) {
if (currentNode != nodeToDelete) {
// Not the node we want to delete yet,
// go on to next node.
previousNode = currentNode;
currentNode = currentNode->next;
continue;
}
// We've now hit the node to delete and know the
// previous node. Fix the structure.
if (previousNode) {
previousNode->next = nodeToDelete->next;
} else {
// No previous node means it's the head node.
headNode = nodeToDelete->next;
}
// The node is now delinked from list. Delete it.
free(nodeToDelete);
// Stop the loop.
break;
}
This is pretty bad performance-wise, which is why there are double-linked lists. There, the whole operations looks like this:
if (nodeToDelete->previous) {
nodeToDelete->previous->next = nodeToDelete->next;
}
if (nodeToDelete->next) {
nodeToDelete->next->previous = nodeToDelete->previous;
}
if (nodeToDelete == headNode) {
headNode = nodeToDelete->next;
}
free(nodeToDelete);
As you can see, no iteration is necessary here as each node knows its previous and next nodes.
BTW, to work these things out (they are pretty basic) it helps to draw a short linked list on a piece of paper. Draw boxes, in each box write the member names (like previous and next) and draw lines from these members to the corresponding other boxes. Then think about what is necessary to do in order to delete the node. It really helps you understand how this works.
The head of the list should never change unless the node you're deleting is the head. You should be moving the temp pointer down the list. You also need to fix up the links when you delete a node, note that there are three cases you need to be aware of and the case where it's the first node requires special handling. I'll give you the skeleton for the code, but since I don't know whether your list is singly- or doubly-linked, I'll leave the pointer updates to you.
temp = headPtr;
prev = NULL;
while (temp != NULL)
{
if (strcmp(temp->fname, stdfname) == 0)
&& strcmp(temp->sname, stdsname) == 0)
{
if (prev == NULL) { // head node
...
}
else if (temp->next == NULL) { // tail node
...
}
else { // interior node
...
}
break; // stop when done
}
prev = temp;
temp = temp->next;
}