[英]How binary search tree insertion works using recursion?
我在理解二叉搜索樹插入的遞歸部分時遇到了一些麻煩。
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
這樣這段代碼就不適用於高度> 2的樹。
但是,它適用於任意數量的節點。
我必須在這里遺漏一些關鍵細節。
假設我想在BST中插入25,即插入(root,25);
如25> 15: -
我在這里打破了遞歸部分:
root->right = insert(root->right, 25);
或15->right = insert(15->right,25);
在這里,再次遞歸調用它,因為25> 20
insert(root->right, 25)
=> root->right->right
= insert(root->right->right, 25);
或insert(15->right, 25)
=> 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]
你忘了root-> right是你以root身份傳遞給函數的地址的root-> right。 每次調用以root-> right或root-> left插入遍歷,具體取決於您遍歷的方式。
這個陳述不正確:
root->right = root->right->right = tmp;
一旦返回函數的迭代,它就會從堆棧中刪除,所以在這種情況下我們有3個調用,我將把你的數字放在指針值的位置。
insert(15->right,25)
insert(20->right,25)
最后一個為null所以它創建了25的節點並將其返回到調用插入(20-> right,25)並將25設置為20-> right,這樣你就有了一個看起來像這樣的樹
/* consider following BST with their addresses[]:
20 [200]
\
25 [300]
*/
然后它將這個樹返回到調用插入(15-> right,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,所以我們調用(我將再次使用樹的那個節點上的值來表示我們傳遞的指針)insert(15,25)
然后調用root-> right上的insert,恰好是20
insert(20, 25)
這個調用再次插入20個右節點,現在恰好是null
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;
我認為混淆可能來自兩個不同的來源。 首先,樹注釋到您的代碼是不可能的。 其次,只有在空指針中傳遞函數時才會創建新節點。 只有小於15的值可以在左側。 它會是這樣的(取決於添加順序):
15
/ \
20
/ \
30
當你向它添加25時,它將最終如下:
15
/ \
20
/ \
30
/
25
我將嘗試逐步解釋此代碼。 在第一個函數調用時向原始樹添加25時,第一個節點不為NULL,而25> 15則為
else
{
root->right = insert(root->right, data);
}
叫做。 這會以遞歸方式調用相同的插入函數,但現在使用20節點進行比較。 再次不是null並且25> 20所以如上所述在右節點上調用insert。 這再次調用遞歸函數,但現在在30. 25 <30,因此它調用左節點上的函數。 此時,函數已經在NULL指針中傳遞,因為那里沒有任何內容,並且創建了一個新節點並將其放置在此位置。
在某種程度上你是對的。 你永遠不會有一個高度> 2的子樹(不是樹)。
在這段代碼中,你永遠不會有root->right->right
,因為就代碼而言,當你調用root->left = insert(root->left, data);
(本地)根指針現在指向剛剛插入的節點。 (本地)根指向root->left.
因此,您可以擁有任何高度的樹(但是,本地根指針指向高度<2的子樹)
需要注意的是insert()
始終返回root
傳遞給它,除非參數root == NULL
。 因此,您插入的新節點無法“走上樹”。 在遞歸調用中發生的事情並不重要 - 您總是返回在非NULL
情況下傳遞的相同root
。
盡管一些人教授遞歸的方式,我認為它(無論如何我的大腦) 不會嘗試展開遞歸,而是考慮邏輯是否有意義:
如果傳遞非NULL
節點和data < root->data
,如果你執行root->left = insert(root->left, data)
並假設insert()
神奇地“正常工作”,你會得到正確的結果嗎? “(即,它將data
插入左側樹並返回該樹的根)?
如果邏輯檢查左右兩種情況,則考慮基本情況:如果傳遞的是NULL
節點,是否會返回正確的單元素樹?
如果邏輯也檢查了基本情況,那么你知道你的代碼必須是正確的,因為遞歸步驟是有意義的,你知道你將落在一個也有意義的基本情況下(因為你最終會到達一個NULL
節點)當你走在樹上時)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.