我在理解二叉搜索树插入的递归部分时遇到了一些麻烦。
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]
答案 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
节点。)