How to prune tree data structure at given depth in C - c

I am trying to write this function:
struct treeNode *pruneTree(struct treeNode *root, int depth);
Which given a tree like:
1 level 0
/ \
2 3 level 1
/ \ \
4 5 6 level 2
/ \
7 8 level 3
If depth = 1 then create a tree with depth = 1 and cut everything after so the result should be:
1
/ \
2 3 // tree with depth = 1
I know how to write a function that prunes the leaves and I am trying to adapt it to prune at any level:
int isLeaf (struct treeNode * treeNode) {
return (treeNode->left == NULL) && (treeNode->right == NULL);
}
void removeLeaves(struct treeNode * root) {
if (root->left != NULL) {
if (isLeaf (root->left)) {
free(root->left);
}
else {
removeLeaves(root->left);
}
}
if (root->right != NULL) {
if (isLeaf (root->right)) {
free(root->right);
}
else {
removeLeaves(root->right);
}
}
}
What is a good strategy to do this? My approach is to replace the isLeaf function with a isAfterDepth function and using a helper function that calculates the depth, but this doesn't seem efficient. What is a more elegant way to do it?

Copying the tree
If you intend to make a copy of the tree that is pruned at a certain level, you can simply use recursion and at each recursive call decrease the depth parameter with one, if the depth results in 0, you simply do no longer recursively copy the children.
struct treeNode *pruneTree(struct treeNode *root, int depth) { //version where the tree is copied
if(root == NULL || depth < 0) {
return NULL;
} else {
treeNode *copyRoot = (treeNode*) malloc(sizeof(treeNode));
copyRoot->value = root->value;
copyRoot->left = pruneTree(root->left,depth-1);
copyRoot->right = pruneTree(root->right,depth-1);
return copyRoot;
}
}
The code works as follows: if the given root pointer is NULL or th depth is less than zero, NULL is returned, because either we call this with the child of a leaf or the depth constraint has been reached.
If that is not the case, we make a copy of the current node: we allocate a new treeNode object, copy the value of the original node (assuming this is called value), and perform a recursive call to copy the left and right children.
Altering the tree
You can also alter the current tree. In that case, you better first define a function to remove a subtree and all its descendants:
void prune(struct treeNode * root) {
if(root != NULL) {
if (root->left != NULL) {
prune(root->left);
}
if (root->right != NULL) {
prune(root->right);
}
free(root);
}
}
Now we simply need to define a method that will prune only at a certain level:
struct treeNode *pruneTree(struct treeNode *root, int depth) { //version where the tree is altered
if(root != NULL) {
if(depth <= 0) {
prune(root->left);
root->left = NULL;
prune(root->right);
root->right = NULL;
} else {
pruneTree(root->left,depth-1);
pruneTree(root->right,depth-1);
}
}
return root;
}

Related

How to do binary search lookups while a Morris inorder traversal is in progress

Morris inoder tree traversal is an inorder traversal of a binary search tree which uses only O(1) memory (and no recursion), but temporarily modifies (and then restores) some of the ->right pointers of the tree.
Example C code (inspired by the C code here):
#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);
/* find(root, current->data + 2); */
/* find(root, current->data - 2); */
current = current->right;
}
}
}
However, because of the temporary modifications, additional binary tree value lookups don't work. For example, if we uncomment the find(...) calls here, then the following naïve find implementation will fall to an infinite loop if called during the traversal:
int datacmp(int a, int b) { /* Increasing order: -1 means less (a < b). */
return a < b ? -1 : a > b;
}
struct node *find(struct node *root, int data) {
struct node *explore = root;
int c;
while (explore != NULL) {
c = datacmp(data, explore->data);
if (c == 0) {
return explore;
} else if (c < 0) {
explore = explore->left;
} else {
explore = explore->right;
}
}
return NULL;
}
Is there an implementation of find which works even during the Morris inorder traversal?
This implementation of find works outside and during Morris inorder traversal:
int datacmp(int a, int b) { /* Increasing order: -1 means less (a < b). */
return a < b ? -1 : a > b;
}
struct node *find(struct node *root, int data) {
struct node *explore = root;
struct node *milestone = NULL;
int c;
while (explore != NULL) {
c = datacmp(data, explore->data);
if (c == 0) {
return explore;
} else if (c < 0) {
milestone = explore;
explore = explore->left;
} else {
explore = explore->right;
/* Stop on circular path created by a Morris inorder traversal. */
if (explore == milestone) break;
}
}
return NULL;
}
This implementation of binary search tree lookup is still O(depth), but it detects and stops on circular paths created by a pending Morris inorder traversal. It works like this: Such a circular path starts with a node (let's call it milestone), then the path moves left, then the path moves right at least once, reaching the milestone node again. The implementation above records the current node as milestone before each left move, and it checks for reaching the milestone node after each right move.

How to write a recursive function to search for a key in a binary tree using in-order traversal in C?

I am trying to write a recursive function that, given the root of a binary tree and a key, searches for the key using in-order traversal. The function returns NULL if the node with the key isn't found; otherwise it returns the node containing the key.
What I'm having trouble with is returning the node that contains the key. Every time I call the function and the key is in the binary tree, the function returns NULL. It feels like the result keeps getting overwritten by the initialization in the first line in the function.
Here's the in-order search function:
typedef struct treeNode
{
int data;
struct treeNode *left, *right;
} TreeNode, *TreeNodePtr;
typedef struct
{
TreeNodePtr root;
} BinaryTree;
TreeNodePtr inOrderKey(TreeNodePtr root, int key)
{
TreeNodePtr node = NULL;
if (root != NULL)
{
node = inOrderKey(root->left, key);
if(key == root->data)
{
node = root;
return node;
}
node = inOrderKey(root->right, key);
}
return node;
}
Here's the main function:
int main()
{
FILE * in = fopen("numbst.txt", "r");
BinaryTree bst;
bst.root = NULL;
int num;
fscanf(in, "%d", &num);
while (num != 0)
{
if (bst.root == NULL)
bst.root = newTreeNode(num);
else
{
TreeNodePtr node = findOrInsert(bst, num);
}
fscanf(in, "%d", &num);
}
printf("\nThe in-order traversal is: ");
inOrder(bst.root);
printf("\nThe pre-order traversal is: ");
preOrder(bst.root);
TreeNodePtr keyNode;
int count = 0;
keyNode = inOrderKey(bst.root, 9);
if (keyNode != NULL)
count = 1;
else
count = 0;
if (count == 1)
printf("\n\n%d exists in the binary tree. In order traversal was used.\n", 9);
else
printf("\n\n%d doesn't exist in the binary tree. In order traversal was used.\n", 9);
return 0;
}
The in-order traversal of the binary tree I'm working with is: 1 2 3 4 5 7 9 21
The pre-order traversal of the binary tree is: 4 1 2 3 7 5 21 9
I'm testing the function using 9 and 31.
This code:
node = inOrderKey(root->left, key);
if(key == root->data)
{
node = root;
return node;
}
node = inOrderKey(root->right, key);
first uses inOrderKey to search the left subtree. Then it ignores the result.
Then it tests whether the current node contains the key. If it does, it returns to its caller. If the caller was itself (this is in a recursive call, not the original), the caller likely ignores the result.
Then it uses inOrderKey to search the right tree. Then it returns the result.]
Ultimately, the node containing the key will be returned only if it was in the rightmost path. If it is in the left subtree of any node, it will be ignored.
To fix this, after each call to inOrderKey, test whether the returned value is a null pointer. If it is not, return it immediately instead of going on.
The problem you have is that you insist in navigating the whole tree without checking if you found the key already. In
TreeNodePtr inOrderKey(TreeNodePtr root, int key)
{
/* don't declare a local you don't know
* yet if you are going to use */
/* better to check the opposite */
if (root == NULL)
return NULL; /* no tree, nothing can be found */
TreeNodePtr node = inOrderKey(root->left, key);
if (node) return node; /* we found it in the left child */
if(key == root->data) { /* check here */
/* you found it in this node */
return root;
}
/* last chance, right child :) */
return inOrderKey(root->right, key);
}
the verifications are made, so this should work (I've not tested it, as you didn't post a complete and verifiable example, my apologies for that)
[edited - updated to use in order traversal]
Traverse left. If key not found, then check root, if key doesn't match, then recurse right.
TreeNodePtr inOrderKey(TreeNodePtr root, int key)
{
TreeNodePtr node = NULL;
if (root)
{
node = inOrderKey(root->left, key);
if (node) {
return node;
}
if (key == root->data)
{
return root;
}
node = inOrderKey(root->right, key);
}
return node;
}

Binary Tree Insert without ordering

Is there any way to insert a new node in a binary tree (not bst) without comparing key values? The following code only works for the very first three nodes.
node *insert (node *root, int *key) {
if (root==NULL) {
root=newNode(root, key);
return root;
}
else if (root->left == NULL)
root->left=insert(root->left,key);
else if (root-> right == NULL)
root->right=insert(root->right,key);
return root;
}
If you change
else if (root-> right == NULL)
to just
else
Then it would have the effect of always adding to the right.
If you want it to randomly pick, add a call to srand outside this function.
srand(time(NULL));
Then in this function, call
else if (rand() > MAX_RAND / 2) {
root->right = insert(root->right, key);
} else {
root->left = insert(root->left, key);
}
at the end of your existing if/else structure.
See also:
Lack of randomness in C rand()
If your tree tracks its height at each node, you could add after your null checks something like
else if (root->left->height <= root->right->height) {
root->left = insert(root->left, key);
} else {
root->right = insert(root->right, key);
}
That would keep the tree balanced automatically. But it requires additional code to manage the height. E.g.
root->height = 1 + ((root->left->height > root->right->height) ? root->left->height : root->right->height);
I leave it up to you whether that additional overhead is worth it.
The people suggesting using a heap are suggesting using the indexes as the ordering. This is kind of useless as a heap, but it would make a balanced binary tree. So the root node would be the first inserted and the most recent inserted would be the rightmost leaf.
You could just keep track of the height of each node, and always insert it into the side with fewer children:
node *insert (node *root, int *key) {
if (root==NULL) {
root=newNode(root, key);
root->height = 0
}
else if (root->left == NULL) {
insert(root->left,key);
}
else if (root->right == NULL) {
insert(root->right,key);
}
else if (root->left->height <= root->right->height) {
insert(root->left,key);
} else {
insert(root->right,key);
}
root->height++
}
Comparing values is actually irrelevant, the only think you need to do is set a pointer. Since you didn't specify any real requirements, one solution could be as follows:
Changing the signature a bit so now you have a pointer to an already allocated node:
void insertNode(node *&root, node *newNode) {
if (root == NULL) {
root = newNode;
return;
}
if (root->left == NULL) {
root-left = newNode;
return;
}
helperInsert(root->left, newNode);
return;
}
This will set the head (assuming I got the signature right), and otherwise check the left child.
void helperInsert(node *it, node *newNode) {
if (it->left == NULL) {
it->left = newNode;
return;
}
helperInsert(it->left, newNode);
return;
}
This is obviously a flawed approach (the tree will not be balanced at the slightest), almost treating the tree as a linked list, but to my best understanding of the question, this is an example of how it can be done.
In
else if (root->left == NULL)
root->left=insert(root->left,key);
you know root->left is NULL so why to do the recursive call ?
Of course same for the next else
The following code only works for the very first three nodes.
If both left and right are non NULL you do not insert, that time it was necessary to do the recursive call on one of the two branches, and you will consider the key (so insert ordered) to decide which one. Note that the 2 tests to NULL you did are not correct if you insert to have a sorted tree ...
The heap advice is most sound. You don't need to heapify anything, just follow the rules that an element at index k has children at 2*k + 1 and 2*k + 2.
Another approach, useful when there is no array, but the nodes are generated on the fly, is to fill the tree level-wise. Notice that at level k there are 2 ** k slots, conveniently indexed by a k-bit integer. Interpret the index as a path down the tree (clear bit tells to follow left child, set bit tells to follow a right one), along the lines of:
void insert_node(struct node * root, struct node * node, unsigned mask, unsigned path)
{
while ((mask >> 1) != 1) {
root = mask & path? root->right: root->left;
}
if (mask & path) {
assert (root->right == NULL);
root->right = node;
} else {
assert (root->left == NULL);
root->left = node;
}
}
void fill_level_k(struct node * root, unsigned k)
{
unsigned slots = 1 << k;
for (unsigned slot = 0; slot < slots; slot++) {
struct node * node = generate_new_node();
insert_node(root, node, slots, slot);
}
}

Find max and min value of BST

Hello stackoverflowers,
i am facing a problem with my function in C, i want to create a function that give me the min and max value in BST.
The problem is when i use this function it returns the same value for min and max:
void Find_Min_Max(node *bt,int* maxint,int* minint)
{
node *tmp = bt;
if( bt == NULL)
{
*maxint = 0; // Only if the tree contains nothing at all
*minint = 0; // Only if the tree contains nothing at all
}
if( bt->left)
return Find_Min_Max(bt->left,&(*maxint),&(*minint));
*minint = bt->data;
if( tmp->right)
return Find_Min_Max(tmp->right,&(*maxint),&(*minint));
*maxint = tmp->data;
}
But when i use it to give me just one result max/min, i delete this part of code, everything work perfectly:
if( tmp->right)
return Find_Min_Max(tmp->right,&(*maxint),&(*minint));
*maxint = tmp->data;
Any idea how this will work?.
Thank you in advance.
It's not really easy / intuitive to recursively compute max and min at the same time in the same function. I would even say it's not possible, because those are two completely different traversals.
You should have a function to get the minimum, a function to get the maximum, and call each of them inside Find_Min_Max.
This would be a possible approach:
int find_min(node *n) {
if (n == NULL) {
return 0;
}
while (n->left != NULL) {
n = n->left;
}
return n->data;
}
find_max is similar, but traverses right links only:
int find_max(node *n) {
if (n == NULL) {
return 0;
}
while (n->right != NULL) {
n = n->right;
}
return n->data;
}
Then, find_min_max() is easy to code:
void find_min_max(node *bt, int *min, int *right) {
*min = find_min(bt);
*max = find_max(bt);
}
find_min() and find_max() could be recursive, but the iterative approach has the desirable property of using constant memory (and consequently avoids stack overflows).
To find the minimum value in a BST, you follow the chain of left children from the root until you reach a node with no left child. That node contains the minimum value (even if it does have a right child).
The algorithm to find the maximum is exactly the mirror image: follow the chain of right children until you reach a node with no right child. That node contains the maximum value.
It does not make sense to try to perform both traversals at the same time, because they follow completely different paths. If you want a single function to discover both the minimum and the maximum value, then it doesn't make much sense for that function itself to be recursive. It could, however, wrap calls to two separate recursive functions, one to find the minimum, and the other to find the maximum.
Finding min and max value in a BST are very easy. Please check both code snippet below I explain how these codes work.
public int minValueInBST(Node node){
if (node == null) throw new IllegalStateException();
Node current = node;
while (current.leftChild != null) {
current = node.leftChild;
}
return current.value;
}
To find the minimum value in BST we have to find the leftmost leaf node because that node contains the minimum value. So at first, we check the root node is null or not if null we throw IllegalStateException otherwise we find the left node, at last, we return the left node value.
public int maxValueInBST(Node node){
if (node == null) throw new IllegalStateException();
Node current = node;
while (current.rightChild != null) {
current = node.rightChild;
}
return current.value;
}
To find the maximum value in BST we have to find the rightmost leaf node because that node contains the maximum value. So at first, we check the root node is null or not if null we throw IllegalStateException otherwise we find the right node, at last, we return the right node value.
// try this
tree_node *min(tree_node *root)
{
if (!root)
{
printf("Tree is empty");
exit(1);
}
tree_node *ret_val;
if (root->left == NULL)
{
ret_val = root;
}
else
{
ret_val = min(root->left);
}
return ret_val;
}
tree_node *max(tree_node *root)
{
if (!root)
{
printf("Tree is empty");
exit(1);
}
tree_node *ret_val;
if (root->right == NULL)
{
ret_val = root;
}
else
{
ret_val = max(root->right);
}
return ret_val;
}
complete code

Deleting a node from a Binary Tree Search

I think there are multiple errors in my code for deleting a node from a BST. I just can't figure out what! Here's my code. Thanks in advance!
void del(int val){
help = root;
f = help;
while (help){
if(help->data==val) break;
f = help;
if (val > help-> data) help = help->right;
else help = help->left;
} if(help->data != val) printf("\nElement not found!");
else{
printf("Element found!");
target = help;
if(val>f->data){
if(target->right && !target->left) {f->right = target->right; f = target->right;}
else {f->right = target->left; f = target->left;}
} else{
if(target->right && !target->left) {f->left = target->right; f = target->right;}
else {f->left = target->left; f = target->left;}
}
while(help ->right) help = help->right;
if(help->left) help = help->left;
f->right = help;
free(target);
}
}
One error that I spot is that if one were to delete the left node in the tree, then the last few lines might not work since they are not symmetric. So, let us say, the tree has root as 6 and left-child as 4 and right-child as 8. Now, you want to delete 4. So, after you have found the node in the first while clause, you would hit the else clause of "if (val > f->data){". At this point, f would point to 6, target and help would point to 4. Now, you are setting the left of f to right of target, so left of 6 would point to NULL and f itself would point to NULL. So, far so good! But, once you get into the while loop, since help has no right node, help would continue to point to 6. In the end, you make the right node of f (btw, f is already NULL at this point) to help and you would actually end up crashing!
while (help->right)
help = help->right;
if (help->left)
help = help->left;
f->right = help;
Another error is that you do not update the root pointer, incase you end up deleting the root node.
One simpler approach would be to divide this into three cases. I am providing code for all the three cases. Use a sample tree for each of these three cases and then debug/test it.
First, if the found node (which your file while loop is doing) has no child nodes, then delete it and set its parent to NULL and you are done. Here is a rough-first cut of the first case:
/* If the node has no children */
if (!help->left && !help->right) {
printf("Deleting a leaf node \n");
f = help->parent;
if (f) {
if (value > f->value)
f->right = NULL;
else
f->left = NULL;
}
/* If deleting the root itself */
if (f->value == root) {
root = NULL;
}
free(help);
return 0;
}
Second, if the found node has only child (left or right), then splice it out and the child of the found node becomes hte child of the parent node. Here is the second case:
/* If the node has only one child node */
if ((help->left && !help->right)
|| (!help->left && help->right)) {
printf("Deleting a node with only one child \n");
f = help->parent;
if (help->left) {
child_node = help->left;
} else {
child_node = help->right;
}
if (f) {
if (value > f->value) {
f->right = child_node;
} else {
f->left = child_node;
}
} else {
/* This must be the root */
root = child_node;
}
free(help);
return 0;
}
The third case is tricky -- here the found node has two child nodes. In this case, you need to find the successor of the node and then replace the value of the found node with the successor node and then delete the successor node. Here is the third case:
/* If the node has both children */
if (help->left && help->right) {
successor_found = find_successor(help, help->data);
printf("Deleting a node with both children \n");
if (successor_found) {
successor_value = successor_found->value;
del(successor_found->value);
help->value = successor_value;
}
return 0;
}
And, here is the code to find successor:
binary_node_t *node find_successor(binary_node_t *node, int value) {
binary_node_t *node_found;
if (!node) {return NULL; }
node_found = node;
old_data = node->data;
/* If the node has a right sub-tree, get the min from the right sub-tree */
if (node->right != NULL) {
node = node->right;
while (node) {
node_found = node;
node = node->left;
}
return node_found;
}
/* If no right sub-tree, get the min from one of its ancestors */
while (node && node->data <= old_data) {
node = node->parent;
}
return (node);
}
typedef struct xxx {
struct xxx *left;
struct xxx *right;
int data;
} ;
#define NULL (void*)0
#define FREE(p) (void)(p)
void treeDeleteNode1 (struct xxx **tree, int data)
{
struct xxx *del,*sub;
/* Find the place where node should be */
for ( ; del = *tree; tree = (data < del->data) ? &del->left : &del->right ) {
if (del->data == data) break;
}
/* not found: nothing to do */
if ( !*tree) return;
/* When we get here, `*tree` points to the pointer that points to the node_to_be_deleted
** If any of its subtrees is NULL, the other will become the new root
** ,replacing the deleted node..
*/
if ( !del->left) { *tree = del->right; FREE(del); return; }
if ( !del->right) { *tree = del->left; FREE(del); return; }
/* Both subtrees non-empty:
** Pick one (the left) subchain , save it, and set it to NULL */
sub = del->left;
del->left = NULL;
/* Find leftmost subtree of right subtree of 'tree' */
for (tree = &del->right; *tree; tree = &(*tree)->left) {;}
/* and put the remainder there */
*tree = sub;
FREE(del);
}
To be called like:
...
struct xxx *root;
...
treeDeleteNode1( &root, 42);
...

Resources