从二进制搜索树中删除节点时理解递归调用

时间:2015-06-02 12:40:57

标签: c recursion binary-search-tree

使用给定密钥删除BST中的节点的功能如下:

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

考虑具有前序遍历的树为:50 30 20 40 70 60 80

我希望删除节点20.函数调用如下:

deleteNode(root=50, 20) 
deleteNode(root=30, 20)
deleteNode(root=20, 20)

当它到达此调用(deleteNode(20,20))时,执行else部分。自root->left == NULL起,剪切的代码执行为:

temp = root->right (which is NULL)
free (root)
return temp (returning NULL)

它会返回上一个电话root->left = deleteNode(20, 20) = NULL。其他调用(即deleteNode(30, 20) and deleteNode(50, 20))会发生什么,并在执行结束时返回根。实际上返回了哪个根?

2 个答案:

答案 0 :(得分:2)

回答&#34;实际返回了哪些根&#34; ......他们都是。问题是他们返回的地方是什么,最终价值是多少。

当您在代码中向前搜索时,您会看到执行递归函数调用的位置,并了解您创建了具有不同值的函数的新副本,并再次从顶部跟踪。你现在正在解开这个过程。

从您离开的地方开始,您return temp的值为root->left = deletenode(20, 20)的{​​{1}},如您所知。您现在可以从该点恢复执行。 else ifelse条件不会被神奇地重新评估,您可以放到左侧节点设置为NULL的return root。然后将此根返回到deletenode(30,20),然后用返回的值替换左侧或右侧部分。

此模式将继续,直到所有函数调用都已解决。名称root可能令人困惑,但它是&#34; root&#34;在递归期间创建的每个子树的唯一形成原始树的分支。

使用您的值提供一些说明:

       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)

尝试追踪你:

  • deleteNode(&#34;节点50&#34;,20)
  • 如果(root == NULL)为false,则跳过语句(让我们调用root&#34;节点50&#34;)
  • 如果(20 <50)为真,则执行&#34;节点50&#34; - &gt; left = deleteNode(&#34;节点50&#34; - &gt; left,20)
    • 如果(root == NULL)为false,则跳过语句(root此处为&#34;节点50&#34; - &gt; left,或&#34;节点30&#34;)
    • 如果(20 <30)为真,则执行&#34;节点30&#34; - &gt; left = deleteNode(&#34;节点30&#34; - &gt; left,20)
      • 如果(root == NULL)为false,则跳过语句(root为&#34;节点20&#34;)
      • 如果(20 <20)为假,则跳过块
      • 如果(20> 20)为假,则跳过块
      • 其他阻止:
        • if(&#34;节点20&#34; - &gt; left == NULL)为真
        • 使用&#34;节点20&#34;的正确子节点创建临时节点。 (恰好是NULL)
        • 删除&#34;节点20&#34;
        • 返回临时节点的地址
    • 将返回值(NULL)分配给&#34;节点30&#34; - &gt;左
    • 控制流程绕过if结构的其余部分,执行return&#34;节点30&#34;
  • &#34;节点30&#34;已返回,将值分配给&#34;节点50&#34; - &gt;左
  • 控制流程绕过if结构的其余部分,执行return&#34;节点50&#34;

追踪结束。

您返回的树现在看起来像:

         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)

答案 1 :(得分:1)

我认为理解递归函数的“技巧”是首先理解非递归函数,然后认识到递归函数完全相同。

想象一下,您对树的每个级别都有一个函数 - deleteNode_1deleteNode_2deleteNode_3,依此类推。

您可以通过将树的根目录传递给deleteNode_1来进行第一次通话。

如果要删除的节点位于其参数的子树中,deleteNode_1将使用该子树调用deleteNode_2,并将其参数的一个子树替换为结果。

deleteNode_2的调用可能会调用deleteNode_3,依此类推,但deleteNode_1不需要关心这一点,它只关心从{{获取合适的子树1}}。

它们看起来像这样:

deleteNode_2

并按照以下方式实施:

// 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.

如果您了解这些函数的工作原理,那么理解递归函数的步骤非常小。