二叉搜索树插入如何使用递归

How binary search tree insertion works using recursion?

本文关键字:何使用 递归 插入 搜索树      更新时间:2023-10-16

我在理解二叉搜索树插入的递归部分时遇到了一些麻烦。

bstnode* insert(bstnode* root,int data)
{
    if(root==NULL){
        bstnode* tmp= new bstnode();
        tmp->data=data;
        tmp->left=tmp->right=NULL;
        return tmp;
    }
    if(data<root->data)     
        root->left = insert(root->left, data); 
    else 
        root->right = insert(root->right, data); //can't understand the logic here
    return root; 
}
/* consider following BST with their addresses[]:
              15 [100]
             /  
           10    20 [200]
                   
                   tmp [300]  
*/

根据我的说法root->right = insert(root->right, data);应该将新创建的节点的地址存储在root->right中,因此此代码不适用于高度为>2 的树。
但是,它可以完美地适用于任意数量的节点。
我在这里一定错过了一些关键细节。

假设我想在 BST 中插入 25,即插入(根,25(;
作为 25>15:-

我在这里分解递归部分:

root->right = insert(root->right, 25);15->right = insert(15->right,25); 在这里,再次递归调用它,因为 25>20
insert(root->right, 25) => root->right->right = insert(root->right->right, 25);
insert(15->right, 25) => 20->right = insert(20->right, 25);

insert(20->right,25) NULL,因此会创建一个新的节点tmp
insert(20->right,25);返回tmp .

现在展开递归。

//20->right = insert(20->right, 25);

所以

20->right= 300(TMP地址(;

//insert(15->right, 25) => 20->right
//and 15->right = insert(15->right,25);


15->right = 20->next;因此15->right = [300] 地址。
root->right = [300] 地址。
我的方法有什么问题?

再次概述递归调用:

15->right = insert(15->right,25);
15->right = [20->right = insert(20->right,25)]; //20->right is NULL so creating new node
15->right = [20->right=   300 address of tmp];
15->right = [20->right or 300]
15->right = [300] // but in reality 15->right = [200]

您忘记了 root->right 是您作为 root 传递给函数的地址的 root->right。 每次插入调用都以 root->right 或 root->left 传递,具体取决于遍历的方式。

此语句不正确:

root->right = root->right->right = tmp;

返回函数的迭代后,它将从堆栈中删除,因此在这种情况下,我们有 3 次调用,我会将您的数字放在指针值的位置。

insert(15->right,25)
insert(20->right,25) 

最后一个是 null,因此它创建具有 25 的节点并将其返回到调用插入(20->right,25(,并将 25 设置为 20->right,因此您有一个如下所示的树

/* consider following BST with their addresses[]:
              20 [200]
               
                25 [300]  
*/

然后,它将此树返回到调用 insert(15->right,25(,并将该树设置为我们刚刚返回的树,以便我们得到最终的树

/* consider following BST with their addresses[]:
          15 [100]
         /  
       30    20 [200]
               
                25 [300]  
*/

编辑:让我看看我是否可以澄清。 让我们再看看你的树

/* consider following BST with their addresses[]:
          15 [100]
         /  
       10    20 [200]
               
               tmp [300]  
*/

我们想插入 25,所以我们调用(我将再次使用树的那个节点上的值来表示我们正在传递的指针( 插入(15, 25(

然后调用 insert on root->right,恰好是 20

insert(20, 25)

此调用现在在 20 个右侧节点上再次插入,恰好为空

insert(null,25)

所以现在让我们看看回报

insert(null,25( 返回一个包含 25 的节点,然后从堆栈中删除

 return 25;

insert(20,25( 获取其对 25 节点的返回。 它将右子项设置为 25,如下所示

 20->right = 25;
 return 20;

现在我们回到 insert(15,25( 的原始调用。 它返回了 20。 所以它确实如此

15->right = 20;
return 15; 

我认为这种困惑可能来自两个不同的来源。首先,注释到代码中的树是不可能的。其次,仅当函数在空指针中传递时,才会创建新节点。只有小于 15 的值才能转到左侧。它会是这样的(取决于添加顺序(:

   15
  /  
     20
    /  
       30

当您添加 25 时,它将如下所示:

   15
  /  
     20
    /  
        30
       /
      25

我将尝试逐步完成代码来解释。当在第一个函数调用上将 25 添加到原始树时,第一个节点不是 NULL,25> 15,因此

else
{ 
    root->right = insert(root->right, data);
}

被称为。这会递归调用相同的插入函数,但现在使用 20 节点进行比较。同样不是空和 25> 20,所以如上所述在右侧节点上调用插入。这再次调用递归函数,但现在在 30 上。25<30,因此它在左侧节点上调用函数。此时,函数已在 NULL 指针中传递,因为那里没有任何内容,因此创建一个新节点并将其放置在该位置。

请注意,除非root == NULL,否则insert()始终返回作为参数传递给它的root。因此,您插入的新节点无法"向上走"。递归调用中发生的情况并不重要 - 您始终返回在非NULL情况下传递的相同root

尽管有些人教授递归的方式,但我认为(无论如何对我的大脑(不要试图展开递归,而是考虑逻辑是否有意义,这是有帮助的:

如果您传递了一个非NULL节点并data < root->data,如果您执行root->left = insert(root->left, data)并假设insert()神奇地"正常工作"(即,它将data插入左树并返回该树的根(,您会得到正确的结果吗?

如果

逻辑同时检查左情况和右情况,则考虑基本情况:如果传递了NULL节点,是否会返回正确的单元素树?

如果逻辑也检查出基本情况,那么你就知道你的代码一定是正确的,因为递归步骤是有意义的,并且你知道你将落在一个也有意义的基本情况中(因为你最终会到达一个NULL节点当你走下树(。

在某种程度上你是对的。您永远不能有高度为>2 的子树(不是树(。

在此代码中,您将永远不会有root->right->right因为就代码而言,当您调用 root->left = insert(root->left, data);

(本地(根

指针现在指向刚刚插入的节点。(本地(根指向root->left.

因此,您可以拥有任何高度的树(但是,本地根指针指向高度 <2( 的子树(