簡體   English   中英

用遞歸方法計算二叉樹的高度的原理是什么?

[英]What is the principle of calculating the height of binary tree by recursive method?

這是可以找到二叉樹高度的代碼。

int PostOrderGetHeight(BinTree BT) { 
   int HL,HR,MaxH; 
   if(BT){ 
   HL = PostOrderGetHeight(BT->Left); 
   HR = PostOrderGetHeight(BT->Right); 
   MaxH = (HL>HR)?HL:HR;
   return (MaxH+1); 
   }
   else return 0;
}

我的困惑是為什么HL,HR,MaxH不需要初始化,以及此遞歸算法如何計算HL和HR。

您在這里所做的是計算每棵樹兩側的高度。 每一個二叉樹都將有一個左右樹,它們將具有自己的高度,直到到達兩端都沒有高度的終端節點。

在此處輸入圖片說明

讓我們來看這個例子。

  • 我們通過A。
  • 它嘗試計算B(a的左樹)的高度。
    • B嘗試計算D的高度(它是左樹)。
      • D嘗試計算其左樹的高度,並返回零。
      • D嘗試計算其右樹的高度(e)
      • e沒有左右樹(均返回零),因此其高度返回(0)+1的最大值
    • D的左右樹的最大值為1,因此D的高度為1 + 1 = 2。
    • B沒有右節點,因此返回零。
    • B的左右節點的最大值為2,因此B返回2 +1 = 3。
    • A計算C的高度(它是正確的節點,將返回1)
    • a的左右最大為3 + 1 = 4。

如果BinTree BT不等於NULL則由於這些語句, HLHR獲得其值

   HL = PostOrderGetHeight(BT->Left); 
   HR = PostOrderGetHeight(BT->Right);  

否則,如果例如BTBT->LeftBT->Right等於NULL則函數調用例如

PostOrderGetHeight(BT->Left);

返回0,因為該函數的定義類似於

int PostOrderGetHeight(BinTree BT) { 
   int HL,HR,MaxH; 
   if(BT){ 
      //...
   }
   else return 0;
        ^^^^^^^^ 
}

在函數的遞歸調用的每次迭代中,選擇左子樹和右子樹之間的最大長度。

我們可以通過PostOrderGetHeight方式為任何樹提供PostOrderGetHeight作品:

  • 證明它適用於高度為0的樹,並且
  • 證明如果它適用於高度為h -1或更小的樹,且h > 0,則適用於高度為h的樹。

因此,我們將證明它適用於高度為0的樹,因此適用於高度為1的樹,因此適用於高度為2和3的樹,依此類推。

為了證明第一部分,假設用高度為0的樹調用PostOrderGetHeight 。這意味着其根節點BT為空。 在這種情況下,當調用例程時, else return 0; 語句執行,並且例程返回零,這是正確的。

為了證明第二部分,假設使用高度為h的樹(其中h > 0)調用PostOrderGetHeight

然后執行以下兩個語句:

HL = PostOrderGetHeight(BT->Left); 
HR = PostOrderGetHeight(BT->Right);

觀察其中的每一個都使用BT子樹調用PostOrderGetHeight 每個子樹BT->LeftBT->Right必須是高度小於h的樹。 由於我們假設PostOrderGetHeight適用於高度為h @ -1的樹,因此它返回這些樹的正確高度。

那么MaxH = (HL>HR)?HL:HR; MaxH設置為左子樹的高度或右子樹的高度中的較大者。 在這一點上,我們知道BT高度比最大值大一,因為它包括更大的子樹和一個更多的節點(根節點BT )。

然后該語句return (MAXH+1); 返回該高度。

#include<stdio.h> 
#include<stdlib.h> 


struct node  
{ 
    int data; 
    struct node* left; 
    struct node* right; 
}; 

int maxDepth(struct node* node)  
{ 
   if (node==NULL)  
       return 0; 
   else 
   { 

       int lDepth = maxDepth(node->left); 
       int rDepth = maxDepth(node->right); 


       if (lDepth > rDepth)  
           return(lDepth+1); 
       else return(rDepth+1); 
   } 
}  


struct node* newNode(int data)  
{ 
    struct node* node = (struct node*) 
                                malloc(sizeof(struct node)); 
    node->data = data; 
    node->left = NULL; 
    node->right = NULL; 

    return(node); 
} 

int main() 
{ 
    struct node *root = newNode(1); 

    root->left = newNode(2); 
    root->right = newNode(3); 
    root->left->left = newNode(4); 
    root->left->right = newNode(5);  

    printf("Height of tree is %d", maxDepth(root)); 

    getchar(); 

} 

希望這可以幫助!

一步聲明和初始化變量可能更清楚。 有關更多信息,請參見此處

int PostOrderGetHeight(BinTree BT) { 
   if(BT){ 
       const int HL = PostOrderGetHeight(BT->Left); 
       const int HR = PostOrderGetHeight(BT->Right); 
       const int MaxH = (HL>HR)?HL:HR;
       return (MaxH+1); 
   } else {
       return 0;
   }
}

如果一個節點沒有其他leftright節點,則此分支的結尾,並返回0

讓我們逐步瀏覽一棵樹(A),其中根具有左(B)和右(C)節點,而其左節點具有左節點(D)。 所有其他分支均為NULL。

我們將A傳遞給PostOrderGetHeight ,后者在其兩個節點(B)和(C)上調用PostOrderGetHeight

PostOrderGetHeight上調用B,它調用PostOrderGetHeight在其左側節點d,並在NULL(這是它的右節點)。

這在D上調用PostOrderGetHeight ,D在其兩個NULL節點上調用PostOrderGetHeight 請注意,對NULL調用PostOrderGetHeight返回0。

在此調用中, PostOrderGetHeight(D) ,HL和HR設置為零。 因此,這有效地初始化了它們。 然后MaxH設置為零,從而有效地對其進行初始化。 然后對PostOrderGetHeight(D)的調用返回PostOrderGetHeight(D) MaxH+1 ,即1。

請記住, PostOrderGetHeight(D)是由被稱為PostOrderGetHeight(B)所以它得到這個答案1設置HL為1 B的右支為NULL,因此HR被設置為0, MaxH被設置為1,所以PostOrderGetHeight(B)返回2。

PostOrderGetHeight(A)稱為PostOrderGetHeight(B)PostOrderGetHeight(C) 由於C有兩個NULL分支,調用PostOrderGetHeight(C)返回1。這將設置HL從返回2 PostOrderGetHeight(B)HR從返回1 PostOrderGetHeight(C)使得MaxH設置為2。因此PostOrderGetHeight(A)將返回3,即樹A的高度。

通用遞歸算法依賴於以下事實:二叉樹的高度大於其左,右子樹的高度加1。空樹的高度為0。

這是一種非常基本的遞歸。 平凡的情況是0,更復雜的情況取決於對問題的各個部分進行相同的處理,然后將它們組合在一起(平凡情況除外)!

這有點像說一堵磚牆的高度是指一磚的高度減去一堵磚牆再減去一堵,沒有一堵牆的高度為0。好的,這是一種瘋狂的測量牆的方法,但是在計算中並不少見!

BinTree將是一個指針。 因此,如果BTNULL (空樹)並且函數返回高度為零(直接跳轉到else return 0 ), if(BT)行將為false。

注意: return 0; 會在這里做。 無需else因為if語句的兩端都將返回。

因此,現在您看一下if-true分支的細節。

HL = PostOrderGetHeight(BT->Left); 
HR = PostOrderGetHeight(BT->Right); 
MaxH = (HL>HR)?HL:HR;
return (MaxH+1); 

HL (表示左側的高度)是從節點到樹的左側的高度, HR是右側的高度。

MaxH = (HL>HR)?HL:HR使用三元運算符(如果表達式為內聯),如果HL>HR則該表達式的值為HL否則為HR 這給了我們左子樹和右子樹的最大高度。

return語句( return (MaxH+1); )加1。

如評論中所述,最好省略:

int HL,HR, MaxH;

和寫

int HL = PostOrderGetHeight(BT->Left); 
int HR = PostOrderGetHeight(BT->Right); 
int MaxH = (HL>HR)?HL:HR;

在C99(1999年標准)之前,您需要在函數開始時聲明所有變量。 這使編譯器可以輕松確定需要多少堆棧。

但是,除非受制於施加該規則的編譯器,否則它不應成為首選樣式(甚至不是所有20世紀的編譯器都這樣做)。

它具有較低的可讀性,並冒着使用未初始化數據(例如未使用的數據塊)的風險,從而降低了安全性。

稍微模糊的是,它也可能阻止編譯器看到優化堆棧布局的機會。

簡而言之,我想知道這段代碼是為老式編譯器還是由舊定時器編寫的。 我不稱呼任何人為老朋友。 我在那些編譯器上編寫了C代碼。 這不是侮辱。

沒有提供BinTree的定義,但它必須類似於:

typedef struct BinTree_tag;

typedef struct {
    struct BinTree_tag *Left;
    struct BinTree_tag *Right;
} *BinTree ;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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