簡體   English   中英

二叉搜索樹插入如何使用遞歸工作?

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM