繁体   English   中英

递归地将元素插入二叉树

[英]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;
}

我希望这是足够的代码。 NodeTree是两个独立的类。 我在绕递归时遇到困难。

我在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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM