[英]Recursively Insert Element Into A Binary Tree
所以我完成了List练习并继续使用Binary Trees。 我的代码到目前为止:
tree.h中
#include "Node.h"
class Tree
{
private:
int mCount;
Node *root;
public:
Tree();
~Tree();
void insert(int, Node *);
};
Tree.cpp
void Tree::insert(int data, Node *node)
{
if( root == 0 )
{
Node *temp = new Node;
temp->setData(100);
temp->setRight(0);
temp->setLeft(0);
root = temp;
}
else
{
if( data > root->getData() )
return insert( data, root->getRight() );
else
return insert( data, root->getLeft() );
}
}
main.cpp中
int main(int argc, char** argv)
{
Tree *tree = new Tree;
tree->insert( 100, 0 );
std::cin.get();
return 0;
}
我希望这是足够的代码。 Node
和Tree
是两个独立的类。 我在绕递归时遇到困难。
我在Tree类中定义了Node *root
在树的顶部有一个根节点。 但是,我看到它的方式,当我在main中调用tree->insert
insert时,我不必指定任何节点。 Tree类的根将完成所有工作。 但是,当我在代码中并且需要重复时,我突然变成一个参数,如上所示。
我的解决方案是将参数Node *node
放在insert()
的参数列表中,然后用main中的0
调用它。 我还需要调用tree->display(0);
作为Node *node
参数。
这看起来很骇人听闻。 我错过了一些明显的东西吗
几点:
首先,不要使用Node**
。 那错误“丑化”了你的代码。 如果确实需要,请使用Node*&
(请参阅此处的答案)。
其次,您不需要递归调用(除非您想使用一个)。
非递归插入方法:
void Tree::insert(int data)
{
if(!root)
{
root = new Node(data); // Node constructor should receive
// the data value and init internal value from it
// it should also set left and right pointers to 0
return;
}
Node* insertIterator = root;
Node* parent = 0;
while(insertIterator)
{
parent = insertIterator;
insertIterator = data < insertIterator->getData()
? insertIterator->getLeft()
: insertIterator->getRight();
}
if(data < parent->getData())
parent->setLeft( new Node(data) );
else
parent->setRight( new Node(data) );
}
如果你使用递归方法,使用该发现的,而不是执行插入递归方法将插入点,递归方法。 基本上,用一个单独的方法替换上面代码中的while循环(我的代码中的FindInsertionPoint
):
Node* Tree::FindInsertionPoint(int data, Node * parent) // this should be private
{
Node* insertPoint = data < parent.getData()
? parent->getLeft()
: parent->getRight();
return insertPoint
? FindInsertionPoint(data, insertPoint)
: parent;
}
void Tree::Insert(int data) // this should be public
{
if(!root)
{
root = new Node(data);
return;
}
Node* parent = FindInsertionPoint(data, root);
if(data < parent.getData())
parent->setLeft(new Node(data)); // see comment on Node constructor above
else
parent->setRight(new Node(data)); // see comment on Node constructor above
}
编辑 :
我在绕递归时遇到困难。
看看它是这样的:要找到插入点 ,您知道需要插入左侧或右侧子节点的子节点 。 要插入左侧, 您需要插入当前节点的左子节点的左子节点或子子节点的子节点。 也就是说,如果你向左边插入,则调用找到左边孩子的插入点部分; 否则,调用查找右子节点的插入点 。
您需要做什么来定义递归算法:
识别适用于部分数据的算法(在这种情况下, 您需要插入左侧或右侧子节点的子节点 )。
识别停止条件(算法何时停止?)。 如果你不这样做,你会得到无限递归和stackoverflow错误:)。
识别算法的可变部分(这应该告诉你递归函数将具有哪些参数)。
您当前在Tree::insert
根本没有使用node
参数,这实际上意味着如果您已经拥有根节点,它将无限递归。
最好的解决方案是定义一个没有node参数的公共插入方法,该方法使用root参数调用另一个私有insert方法,而root参数又以递归方式调用自身。 这样,您的API就是干净的,不允许客户端直接(和不正确地)将元素插入到子树中。
请注意,参数本身必须更改为
Node**
Node*&
,因为在插入时,您希望修改父节点中的指针。
[更新]此外,建议向Node
添加一个构造函数,该构造函数获取数据值并将其左右指针初始化为0.这样可以显着简化调用者代码。 [/更新]
所以最终结果看起来像这样:
void Tree::insert(int data)
{
return insert( data, root );
}
void Tree::insert(int data, Node *&node)
{
if( node == 0 )
{
node = new Node(data);
}
else
{
if( data > node->getData() )
return insert( data, node->getRight() );
else
return insert( data, node->getLeft() );
}
}
佩特给出了一个非常好的例子。 我唯一看到的是temp-> setData(100)应该是temp-> setData(data);
但是,我想关注这一部分:
我在绕递归时遇到困难。
当你第一次介绍递归时,让你的思维以这种方式工作可能有点困难。 我们倾向于想要将算法作为一个整体来考虑顺序步骤的有序列表。 让你的思维考虑它的最好方法是把它画出来。 做得足够,它将成为第二天性。
让我们考虑一下Péter的例子(忽略纠正setData行的一点点遗漏)。 我们只有两种非常简单的情况:
1) 我们在存在的节点上调用insert。 在这种情况下,我们将插入的值与节点的值进行比较。 如果它大于节点值,我们插入到右边的子节点,否则插入到左边。 现在,您只需对要插入的子项重复此过程。
2) 我们在不存在的节点上调用insert。 在这种情况下,我们创建节点并将其值设置为插入值。 结束:递归完成。 这被称为基本案例或一般解决方案,因为它是我们停止的地方。
就是这样 - 它实际上非常简单。 诀窍不是要考虑整个树上发生了什么,而是一次只考虑一个节点,并考虑具有该节点的所有情况。 当你能够以这种方式思考时,我们发现只有两种情况(如果你想要迂腐,则有三种情况,但有两种情况)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.