简体   繁体   中英

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 (ie 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM