簡體   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