is this Ternary expression redundant in splay function - c

https://www.geeksforgeeks.org/splay-tree-set-1-insert/
When I was learning about splay trees, I referred to this article and had a little doubt about the code in it.
this is splay function in this article
struct node *splay(struct node *root, int key)
{
// Base cases: root is NULL or key is present at root
if (root == NULL || root->key == key)
return root;
// Key lies in left subtree
if (root->key > key)
{
// Key is not in tree, we are done
if (root->left == NULL) return root;
// Zig-Zig (Left Left)
if (root->left->key > key)
{
// First recursively bring the key as root of left-left
root->left->left = splay(root->left->left, key);
// Do first rotation for root, second rotation is done after else
root = rightRotate(root);
}
else if (root->left->key < key) // Zig-Zag (Left Right)
{
// First recursively bring the key as root of left-right
root->left->right = splay(root->left->right, key);
// Do first rotation for root->left
if (root->left->right != NULL)
root->left = leftRotate(root->left);
}
// Do second rotation for root
return (root->left == NULL)? root: rightRotate(root);
}
else // Key lies in right subtree
{
// Key is not in tree, we are done
if (root->right == NULL) return root;
// Zag-Zig (Right Left)
if (root->right->key > key)
{
// Bring the key as root of right-left
root->right->left = splay(root->right->left, key);
// Do first rotation for root->right
if (root->right->left != NULL)
root->right = rightRotate(root->right);
}
else if (root->right->key < key)// Zag-Zag (Right Right)
{
// Bring the key as root of right-right and do first rotation
root->right->right = splay(root->right->right, key);
root = leftRotate(root);
}
// Do second rotation for root
return (root->right == NULL)? root: leftRotate(root);
}
}
Here is a snippet from the source code return (root->left == NULL)? root: rightRotate(root);, it appears in the last paragraph of the function.
My question is if I can write like thisreturn rightRotate(root), Because we have already judged whether this pointer is NULL above, It seems to me that after we do the rotate operation, the root->left can not be NULL. If this pointer can be null, hope to point out the situation that does not hold

is this Ternary expression redundant in splay function
No, root = rightRotate(root); and root = leftRotate(root); change root.

Related

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);
}
}

How recursion works for this binary search tree algorithm

I have the following binary search tree, with root node 20. The question I'm trying to answer is, if we apply the function t = deleteRoot(t), what will be the value of the new root node, as well as its immediate left and right children (e.g. the current root node is 20 with immediate left child 11 and immediate right child 32). I scribbled at least 10 pages over the last 2 hours trying to solve this, but the recursion is killing me. Can someone please help me visualize this - i.e some way of thinking which allows me to deal with recursion. I'm not very good at visualizing how recursion works but I can somewhat implement it. Thanks very much. By the way, the answer is root node: 21 with left child 11 and right child 32.
My attempt:
The question tells us to start with t=deleteRoot(t).
parent = node containing 25
succ = node containing 21
succval = 21
then we get to
t = TreeDelete(t, succval)
then I get a bit lost as to how the TreeDelete function works
t->left = TreeDelete(t->left, key) ...etc
deleteRoot(Tree t) function
// delete root of tree
Tree deleteRoot(Tree t)
{
Link newRoot;
// if no subtrees, tree empty after delete
if (t->left == NULL && t->right == NULL) {
free(t);
return NULL;
}
// if only right subtree, make it the new root
else if (t->left == NULL && t->right != NULL) {
newRoot = t->right;
free(t);
return newRoot;
}
// if only left subtree, make it the new root
else if (t->left != NULL && t->right == NULL) {
newRoot = t->left;
free(t);
return newRoot;
}
// else (t->left != NULL && t->right != NULL)
// so has two subtrees
// - find inorder successor (grab value)
// - delete inorder successor node
// - move its value to root
Link parent = t;
Link succ = t->right; // not null!
while (succ->left != NULL) {
parent = succ;
succ = succ->left;
}
int succVal = succ->value;
t = TreeDelete(t,succVal);
t->value = succVal;
return t;
}
Tree TreeDelete(Tree t, Key k) function:
Tree TreeDelete(Tree t, Key k)
{
Tree deleteRoot(Tree);
if (t == NULL)
return NULL;
int diff = cmp(k,key(t->value));
if (diff == 0)
t = deleteRoot(t);
else if (diff < 0)
t->left = TreeDelete(t->left, k);
else if (diff > 0)
t->right = TreeDelete(t->right, k);
return t;
}
By the way, the answer is root node: 21 with left child 11 and right child 32.
Well, it is one solution but not the only solution.
18 with left child 11 and right child 32 will also be a solution.
When deleting the root, the new root can be selected as
The highest number in the left subtree
or
The lowest number in the right subtree
So copy the value of the selected node to the current root. And then delete the selected node. If the selected node has children, you can repeat the process recursively.
In the case of deletion, if the node to be deleted has two children:
Find the successor or the predecessor
Replace the node with the contents of the successor/predecessor
Call delete on the successor/predecessor (this way the size will be decremented)
Return the node
The successor is the highest element on the right subtree while the predecessor is the lowest element on the left subtree. In your case, the successor would be 21 and the predecessor would be 18.

How to prune tree data structure at given depth in 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;
}

Understanding recursive calls in deleting a node from a Binary Search Tree

The function to delete a node in a BST with the given key is as follows:
struct node* deleteNode(struct node* root, int key)
{
// base case
if (root == NULL) return root;
// If the key to be deleted is smaller than the root's key,
// then it lies in left subtree
if (key < root->key)
root->left = deleteNode(root->left, key);
// If the key to be deleted is greater than the root's key,
// then it lies in right subtree
else if (key > root->key)
root->right = deleteNode(root->right, key);
// if key is same as root's key, then This is the node
// to be deleted
else
{
// node with only one child or no child
if (root->left == NULL)
{
struct node *temp = root->right;
free(root);
return temp;
}
else if (root->right == NULL)
{
struct node *temp = root->left;
free(root);
return temp;
}
// node with two children: Get the inorder successor (smallest
// in the right subtree)
struct node* temp = minValueNode(root->right);
// Copy the inorder successor's content to this node
root->key = temp->key;
// Delete the inorder successor
root->right = deleteNode(root->right, temp->key);
}
return root;
}
Considering the tree having the preorder traversal as: 50 30 20 40 70 60 80
I wish to delete the node 20. The function calls are as follows:
deleteNode(root=50, 20)
deleteNode(root=30, 20)
deleteNode(root=20, 20)
When it reaches this call (deleteNode(20, 20)), the else part is executed. Since root->left == NULL, the code snipped is executed as:
temp = root->right (which is NULL)
free (root)
return temp (returning NULL)
It is returned to the last call root->left = deleteNode(20, 20) = NULL. What happens to the other calls (i.e. deleteNode(30, 20) and deleteNode(50, 20)) and at the end of the execution the root is returned. Which of the roots are actually returned?
To answer "which of the roots is actually returned" ... they all are. The question is where are they returned to and what is the final value.
When you traced forwards through your code you saw where the recursive function call was executed and understood that you created a new copy of the function with different values and traced your way from the top again. You are now unwinding that process.
Following from where you left off, you return temp whose value is NULL to the function call for root->left = deletenode(20, 20) as you realized. You now resume execution from that point. The else if and else conditions are not magically re-evaluated, you drop to return root whose left node was set to NULL. This root is then returned to deletenode(30,20) and the left or right portion is then replaced with the returned value.
This pattern continues until all the function calls have resolved. The name root may be confusing but it is the "root" of each sub-tree created during the recursion which only form branches of the original tree.
To provide some illustration, using your values:
50 <- root for deletenode("node 50", x)
/ \
30 70 <- root for deletenode("node 50"-> left or right, x)
/ \ / \
20 40 60 80 <- root for deletenote("node 50"->left/right->left/right,x)
An attempt at a trace for you:
deleteNode( "node 50", 20 )
if ( root == NULL) is false, skip statement (let's call root "node 50")
if ( 20 < 50 ) is true so execute "node 50"->left = deleteNode( "node 50"->left, 20 )
if ( root == NULL ) is false, skip statement (root here is now "node 50"->left, or "node 30")
if ( 20 < 30 ) is true so execute "node 30"->left = deleteNode( "node 30"->left, 20)
if ( root == NULL) is false, skip statement (root is "node 20")
if ( 20 < 20 ) is false, skip block
else if ( 20 > 20 ) is false, skip block
else block:
if ( "node 20"->left == NULL ) is true
create a temp node with the right child of "node 20" (happens to be NULL)
delete "node 20"
return the address of the temp node
assign the returned value (NULL) to "node 30"->left
control flow bypasses the rest of the if structure, execute return "node 30"
"node 30" was returned, assign the value to "node 50"->left
control flow bypasses the rest of the if structure, execute return "node 50"
End of trace.
Your returned tree now looks something like:
50 <- root for deletenode("node 50", x)
/ \
30 70 <- root for deletenode("node 50"-> left or right, x)
/ \ / \
NULL 40 60 80 <- root for deletenote("node 50"->left/right->left/right,x)
I believe the "trick" to understanding recursive functions is to first understand non-recursive functions and then realise that recursive functions work exactly the same.
Imagine that you have one function for each level of the tree - deleteNode_1, deleteNode_2, deleteNode_3, and so on.
You would make your first call by passing the root of your tree to deleteNode_1.
If the node to be deleted is in a subtree of its parameter, deleteNode_1 calls deleteNode_2 with that subtree and replaces one of its parameter's subtrees with the result.
The call to deleteNode_2 might call deleteNode_3, and so on, but deleteNode_1 doesn't need to care about that, it only cares about getting a suitable subtree back from deleteNode_2.
They would look like this:
// Delete a node when 'current' is at level 1
struct node* deleteNode_1(struct node* current, int key);
// Delete a node when 'current' is at level 2
struct node* deleteNode_2(struct node* current, int key);
// Delete a node when 'current' is at level 3
struct node* deleteNode_3(struct node* current, int key);
// .. and so on, as many as you need.
and be implemented like this:
struct node* deleteNode_1(struct node* current, int key)
{
if (current == NULL) return current;
if (key < current->key)
current->left = deleteNode_2(current->left, key);
else if (key > current->key)
current->right = deleteNode_2(current->right, key);
else
{
if (current->left == NULL)
{
struct node *temp = current->right;
free(current);
return temp;
}
else if (current->right == NULL)
{
struct node *temp = current->left;
free(current);
return temp;
}
struct node* temp = minValueNode(current->right);
current->key = temp->key;
current->right = deleteNode_2(current->right, temp->key);
}
return current;
}
struct node* deleteNode_2(struct node* current, int key)
{
if (current == NULL) return current;
if (key < current->key)
current->left = deleteNode_3(current->left, key);
else if (key > current->key)
current->right = deleteNode_3(current->right, key);
else
{
if (current->left == NULL)
{
struct node *temp = current->right;
free(current);
return temp;
}
else if (current->right == NULL)
{
struct node *temp = current->left;
free(current);
return temp;
}
struct node* temp = minValueNode(current->right);
current->key = temp->key;
current->right = deleteNode_3(current->right, temp->key);
}
return current;
}
// ...
If you understand how these functions work, the step is very small to understanding the recursive function.

Binary Search Tree in C: remove node function

I'm putting together functions for a binary search tree and ran into a wall. I'm working on each situation that might be encountered when a node holding a specified value needs to be removed from the tree. I'm uncertain how to handle freeing the node if it does not have a left and right child. The function must return a node. Do I back up, examine each left and right child, and remove the value while it's in a child? But then if the value is in the root, wouldn't I have a similar problem with how to remove it? Just by way of explanation, the program uses a void pointer then casts the TYPE val in a separate function compare() which evaluates both values and returns -1 for <, 0 for ==, and 1 for >.
struct Node *_removeNode(struct Node *cur, TYPE val)
{
if (compare(cur->val, val) == 0) { //if val == cur->val
if (cur->right != NULL && cur->left != NULL) { //if cur has right and left
cur = _leftMost(cur->right);
free(_leftMost(cur->right));
}
else if (cur->right == NULL && cur->left != NULL) { //if cur has left
cur = cur->left;
free(cur->left);
}
else if (cur->right != NULL && cur->left == NULL){ //if cur has right
cur = cur->right;
free(cur->right);
}
else if (cur->right == NULL && cur->left == NULL){ //if cur has no child
//free cur if cur = val
}
}
else if (compare(cur->val, val) == -1) {
cur->right = _removeNode(cur->right, val);
}
else if (compare(cur->val, val) == 1) {
cur->left = _removeNode(cur->left, val);
}
return cur;
}
If the node has neither child then it can simply be deleted. In order to make your recursion in the other cases work, you should return NULL from _removeNode. In all cases, cur should be deleted (freed) as it is no longer needed. In each case, you need to return the replacement subtree. The complication occurs in the first case where the left most descendent of the right child is pulled up. After pulling it up, you need to remove it from the right sub-tree (note that it may be the right sub-tree).
I wrote all of the below off the top of my head so be prepared for a few errors/a bit of debugging. Also, _leftMost and _removeLeftMost can be merged with a bit of work.
The block in question should look something like:
Node *replacement;
if (cur->right != NULL && cur->left != NULL) { //if cur has right and left
replacement = _leftMost(cur->right);
replacement->right = _removeLeftMost(cur->right,replacement);
replacement->left = cur->left;
}
else if (cur->right == NULL && cur->left != NULL) { //if cur has left
replacement = cur->left;
}
else if (cur->right != NULL && cur->left == NULL){ //if cur has right
replacement = cur->right;
}
else if (cur->right == NULL && cur->left == NULL){ //if cur has no child
replacement = NULL;
}
free(cur);
cur = replacement;
The function _removeLeftMost walks down the left child pointers until it sees the node to be replaced and then replaces it with the right child of that node. Something like:
Node *_removeLeftMost(node, remove) {
if (node == remove) {
return node->right; // Note that remove->left should be null
}
else {
node->left = _removeLeftMost(node->left,remove);
return node;
}
}
Also, the main call is something like
root = _removeNode(root, val);
So that handles your concern when the node is the root.

Resources