[英]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.