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

时间:2015-03-17 22:43:54

标签: c++ c algorithm recursion data-structures

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

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中,因此此代码不适用于高度为&gt; 2的树。
但是,它适用于任意数量的节点 我必须在这里遗漏一些重要的细节。

假设我想在BST中插入25,即插入(root,25);
如25> 15: -
我在这里打破了递归部分:
root->right = insert(root->right, 25);
15->right = insert(15->right,25);这里,再次递归调用它,因为25&gt; 20
insert(root->right, 25) =&gt; root->right->right = insert(root->right->right, 25);
insert(15->right, 25) =&gt; 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]

4 个答案:

答案 0 :(得分:2)

你忘记了root-&gt; right是你以root身份传递给函数的地址的root-&gt;右边。每次调用以root-&gt;右或root-&gt;左边插入传递,这取决于你遍历的方式。

此声明不正确:

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

一旦返回函数的迭代,它就会从堆栈中删除,所以在这种情况下我们有3个调用,我将把你的数字代替指针值。

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

最后一个为null所以它创建了25的节点并将其返回到调用插入(20->右,25)并将25设置为20-&gt;右边所以你有一个看起来像这样的树< / p>

/* consider following BST with their addresses[]:

              20 [200]
               \
                25 [300]  
*/

然后将此树返回到调用插入(15->右,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)

然后调用root-&gt;右边的插入,恰好是20

insert(20, 25)

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

insert(null,25)

所以现在让我们来看看回报

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

 return 25;

insert(20,25)返回一个25的节点。它将右边的子节点设置为25,看起来像这样

 20->right = 25;
 return 20;

现在我们回到插入的原始调用(15,25)。它返回20.所以它确实

15->right = 20;
return 15; 

答案 1 :(得分:1)

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

   15
  /  \
     20
    /  \
       30

当您向此添加25时,它最终会如下所示:

   15
  /  \
     20
    /  \
        30
       /
      25

我将尝试逐步解释此代码。当在第一个函数调用上向原始树添加25时,第一个节点不是NULL并且25> 15所以

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

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

答案 2 :(得分:0)

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

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

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

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

答案 3 :(得分:0)

请注意insert()始终返回作为参数传递给它的root,除非root == NULL。因此,您无法插入新的节点,并且#34;走上树&#34;。在递归调用中发生的事情并不重要 - 您总是返回在非root情况下传递的NULL

尽管有些人教授递归,但我认为它(无论如何我的大脑)有助于尝试展开递归,而是考虑逻辑是否有意义:

如果您通过非NULL节点和data < root->data,如果您执行root->left = insert(root->left, data)并假设insert()神奇地&#34},您是否会得到正确的结果?只是工作&#34; (即,它将data插入左侧树并返回该树的根)?

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

如果逻辑检查基本情况,那么你知道你的代码必须是正确的,因为递归步骤是有意义的,你知道你将落入一个也有意义的基础案例(因为你最终会达到当你走在树上时,NULL节点。)