1. 二叉平衡樹
二叉排序樹查找、插入和刪除操作的時間復雜度和樹的深度n有關。構建樹時,當先后插入的結點按關鍵字有序時,二叉排序樹退化為單枝樹,平均查找長度為(n+1)/2,查找效率比較低。提高查找效率,關鍵在於最大限度地降低樹的深度n。因此需要在構建二叉排序樹的過程中進行“平衡化”處理,使之成為二叉平衡樹。
二叉平衡樹,又稱AVL樹。它或者是一棵空樹,或者是具有下列性質的樹:
1) 具備二叉排序樹的所有性質;
2) 左子樹和右子樹深度差的絕對值不超過1;
3) 左子樹和右子樹都是二叉平衡樹。
二叉平衡樹結點的平衡因子定義為左子樹與右子樹的深度之差。二叉平衡樹結點的平衡因子只可能取-1,0,1三個值。含有n個結點的二叉平衡樹的深度與logn同數量級,平均查找長度也和logn同數量級。
二叉平衡樹采用二叉鏈表的結構進行存儲。結構體中增加結點的高度,用以計算結點的平衡因子。結點高度定義:空結點的高度為0;非空結點的高度為以該結點為根結點的樹的高度。
二叉鏈表:
/* * 二叉樹的二叉鏈表存儲結構。 * 額外添加樹的高度,以判斷結點的平衡度。 */ typedef int TElemType; typedef struct BiNode { TElemType data; struct BiNode *lchild; struct BiNode *rchild; int height; }BiNode, *BiTree;
結點高度:
/* * 當T=NULL ,即樹為空樹時,無法通過T->height獲取樹的高度0,所以要額外編寫該函數。 */ int GetHeight(BiTree T) { if (T) return T->height; return 0; }
2. 處理失衡的四種旋轉方式
如何在插入結點的時候進行“平衡化”處理?當在樹中插入一個結點時,檢查樹是否因插入操作而失衡,若失衡,則找出其中的最小不平衡二叉樹,對最小不平衡二叉樹進行調整,以達到新的平衡。最小不平衡二叉樹定義為以離插入結點最近,且平衡因子絕對值大於1的結點作為根結點的樹。
對最小不平衡樹進行調整的操作是旋轉,共有4種旋轉方式LL型,LR型,RL型,RR型,分別介紹如下:
1) LL型(單次右旋)
當根結點左子樹的左子樹中的節點導致根結點的平衡因子為2時,采用LL型旋轉進行調整。圖示為兩種需進行單次右旋的不平衡樹。
LL型旋轉即單次右旋,是將根結點的左孩子作為新的根結點,根結點左孩子的右子樹作為老根結點的左子樹。圖示如下:
注意:旋轉之后,整個樹中只有結點k1,k2的高度發生變化,而x,y,z三棵子樹中所有結點的高度均未發生變化。
代碼:
/* * 當T的左子樹的左子樹上的節點使得T的平衡度為2時,以T為中心進行右旋。 */ bool LLRotate(BiTree *T) { BiTree lc; lc = (*T)->lchild; (*T)->lchild = lc->rchild; lc->rchild = (*T); //注意要更新結點的高度。整個樹中只有*T的左子樹和lc的右子樹發生了變化,所以只需更改這兩棵樹的高度。 (*T)->height = max(GetHeight((*T)->lchild), GetHeight((*T)->rchild)) + 1; lc->height = max(GetHeight(lc->lchild), GetHeight(lc->rchild)) + 1; *T = lc; return true; }
2) RR型(單次左旋)
當根結點右子樹的右子樹中的節點導致根結點的平衡因子為-2時,采用RR型旋轉進行調整。圖示為兩種需進行單次左旋的不平衡樹。RR型旋轉與LL型旋轉相對稱。
RR型旋轉即單次左旋,是將根結點的右孩子作為新的根結點,根結點右孩子的左子樹作為老根結點的右子樹。圖示如下:
注意:旋轉之后,整個樹中只有結點k1,k2的高度發生變化,而x,y,z三棵子樹中所有結點的高度均未發生變化。
代碼:
/* * 當T的右子樹的右子樹上的節點使得T的平衡度為-2時,以T為中心進行左旋。 */ bool RRRotate(BiTree *T) { BiTree rc; rc = (*T)->rchild; (*T)->rchild = rc->lchild; rc->lchild = (*T); //注意要更新結點的高度。整個樹中只有*T的左子樹和lc的右子樹發生了變化,所以只需更改這兩棵樹的高度。 (*T)->height = max(GetHeight((*T)->lchild), GetHeight((*T)->rchild)) + 1; rc->height = max(GetHeight(rc->lchild), GetHeight(rc->rchild)) + 1; *T = rc; return true; }
3) LR型(先單次左旋,再單次右旋)
當根結點左子樹的右子樹中的節點導致根結點的平衡因子為2時,采用LR型旋轉進行調整。圖示為兩種需進行LR型旋轉的不平衡樹。
LR型旋轉是先以根結點的左孩子為中心進行單次左旋,再以根結點為中心進行單次右旋。圖示如下:
代碼:
/* * 當T的左子樹的右子樹上的節點使得T的平衡度為2時, * 先以T的左子樹為中心進行左旋,再以T為中心進行右旋。 */ bool LRRotate(BiTree *T) { RRRotate(&((*T)->lchild)); LLRotate(T); return true; }
4) RL型(先單次右旋,再單次左旋)
當根結點右子樹的左子樹中的節點導致根結點的平衡因子為-2時,采用RL型旋轉進行調整。圖示為兩種需進行RL型旋轉的不平衡樹。RL型旋轉與LR型旋轉相對應。
RL型旋轉是先以根結點的右孩子為中心進行單次右旋,再以根結點為中心進行單次左旋。圖示如下:
代碼:
/* * 當T的右子樹的左子樹上的節點使得T的平衡度為-2時, * 先以T的右子樹為中心進行右旋,再以T為中心進行左旋。 */ bool RLRotate(BiTree *T) { LLRotate(&((*T)->rchild)); RRRotate(T); return true; }
3. 插入操作
插入操作的代碼如下。
/* * 插入操作。 * 如果以*T為根結點的二叉平衡樹中已有結點key,插入失敗,函數返回FALSE; * 否則將結點key插入到樹中,插入結點后的樹仍然為二叉平衡樹,函數返回TRUE。 */ bool AVLInsert(BiTree *T, TElemType key) { BiTree t; //如果當前查找的根結點為空樹,表明查無此結點,故插入結點。 if (!*T) { t = (BiTree)malloc(sizeof(BiNode)); t->data = key; t->height = 1; t->lchild = NULL; t->rchild = NULL; *T = t; return true; } //已有此結點,不再插入。 else if (key == (*T)->data) { return false; } //在左子樹中遞歸插入。 else if (key < (*T)->data) { if (!AVLInsert(&((*T)->lchild), key)) return false; else { //插入成功,修改樹的高度。 (*T)->height = max(GetHeight((*T)->lchild), GetHeight((*T)->rchild)) + 1; //已在*T的左子樹插入結點key,判斷是否需要進行旋轉以保持二叉平衡樹的特性。 if (2 == GetHeight((*T)->lchild) - GetHeight((*T)->rchild)) { //在左子樹的左子樹中插入結點。 if (GetHeight((*T)->lchild->lchild) > GetHeight((*T)->lchild->rchild)) { LLRotate(T); } //在左子樹的右子樹中插入結點。 else { LRRotate(T); } } return true; } } //在右子樹中遞歸插入。 else // (key > (*T)->data) { if (!AVLInsert(&(*T)->rchild, key)) return false; else { //插入成功,修改樹的高度。 (*T)->height = max(GetHeight((*T)->lchild), GetHeight((*T)->rchild)) + 1; //已在*T的右子樹插入結點key,判斷是否需要進行旋轉以保持二叉平衡樹的特性。 if (-2 == GetHeight((*T)->lchild) - GetHeight((*T)->rchild)) { //在右子樹的左子樹中插入結點。 if (GetHeight((*T)->rchild->lchild) > GetHeight((*T)->rchild->rchild)) { RLRotate(T); } //在右子樹的右子樹中插入結點。 else { RRRotate(T); } } return true; } } }
以下圖為例進行兩個關鍵點的說明:進行旋轉的樹為最小不平衡二叉樹;插入結點之后父結點高度的遞歸修正。
假如要在圖一二叉平衡樹中插入結點1,
函數調用步驟:
1 |
調用函數AVLInsert(&9,1)(為表述方便,以&9代表指向結點9的指針); |
2 |
由於1<9,繼續調用AVLInsert(&7,1); |
3 |
由於1<7,繼續調用AVLInsert(&3,1); |
4 |
由於1<3,繼續調用AVLInsert(&2,1); |
5 |
由於2<1,繼續調用AVLInsert(NULL,1),此時由於*T為空樹,增加結點1,並把結點1的高度設置為1,左右孩子分別為空樹,如圖二所示。函數返回TRUE; |
6 |
AVLInsert(NULL,1)函數返回TRUE,並返回至AVLInsert(&2,1),因插入成功,所以更新結點2的高度為max(1,0)+1=2,結點2的平衡因子為1,不進行旋轉操作,函數返回TRUE; |
7 |
AVLInsert(&2,1)函數返回TRUE,並返回至AVLInsert(&3,1),因插入成功,所以更新結點3的高度為max(2,1)+1=3,結點3的平衡因子為1,不進行旋轉操作,函數返回TRUE; |
8 |
AVLInsert(&3,1)函數返回TRUE,並返回至AVLInsert(&7,1),因插入成功,所以更新結點7的高度為max(3,1)+1=4,結點7的平衡因子為2,進行旋轉操作,旋轉之后,更新結點3的高度為2,結點7的高度為2,如圖三所示。函數返回TRUE; |
9 |
AVLInsert(&7,1)函數返回TRUE,並返回至AVLInsert(&9,1),因插入成功,所以更新結點9的高度為max(2,1)+1=4,結點9的平衡因子為1,不進行旋轉操作,函數返回TRUE,插入過程結束。 |
插入的結點一定為葉子結點,插入結點之后依次進行遞歸調用的返回操作,在返回之后,修正父結點的高度(2->3->7->9),之后判斷父結點的平衡因子,當平衡因子超范圍(結點7)時,以該結點為根結點的樹為最小不平衡二叉樹,此時進行旋轉操作。當AVLInsert(&7,1)函數返回之后,以結點7的父結點9為根結點的樹將不再需要進行旋轉操作。因此每次通過函數AVLInsert()插入一個結點時,旋轉操作只在最小不平衡二叉樹中進行一次。已插入結點的父結點的高度是在遞歸過程中依次進行修正的。
4. 刪除操作
刪除操作的代碼如下。
/* * 刪除操作。 * 如果以*T為根結點的樹中存在結點key,將結點刪除,函數返回TRUE, * 否則刪除失敗,函數返回FALSE。 */ bool AVLDelete(BiTree *T, TElemType key) { BiTree pre, post; //沒有找到該結點。 if (!*T) return false; //找到結點,將它刪除。 else if (key == (*T)->data) { //待刪除節點為葉子結點。 if (!(*T)->lchild && !(*T)->rchild) *T = NULL; //待刪除結點只有右孩子。 else if (!(*T)->lchild) *T = (*T)->rchild; //待刪除結點只有左孩子。 else if (!(*T)->rchild) *T = (*T)->lchild; //待刪除結點既有左孩子,又有右孩子。 else { //當待刪除結點*T左子樹的高度大於右子樹的高度時,用*T的前驅結點pre代替*T, //再將結點pre從樹中刪除。這樣可以保證刪除結點后的樹仍為二叉平衡樹。 if (GetHeight((*T)->lchild) > GetHeight((*T)->rchild)) { //尋找前驅結點pre。 pre = (*T)->lchild; while (pre->rchild) { pre = pre->rchild; } //用pre替換*T。 (*T)->data = pre->data; //刪除節點pre。 //雖然能夠確定pre所屬最小子樹的根結點為&pre, //但是不采用AVLDelete(&pre,pre->data)刪除pre,目的是方便遞歸更改節點的高度。 AVLDelete(&((*T)->lchild), pre->data); } //當待刪除結點*T左子樹的高度小於或者等於右子樹的高度時,用*T的后繼結點post代替*T, //再將結點post從樹中刪除。這樣可以保證刪除結點后的樹仍為二叉平衡樹。 else { //尋找后繼節點post。 post = (*T)->rchild; while (post->lchild) post = post->lchild; //用post替換*T。 (*T)->data = post->data; //刪除節點post。 //雖然能夠確定post所屬最小子樹的根結點為&post, //但是不采用AVLDelete(&post,post->data)刪除post,目的是方便遞歸更改節點的高度。 AVLDelete(&((*T)->rchild), post->data); } } return true; } //在左子樹中遞歸刪除。 else if (key < (*T)->data) { if (!AVLDelete(&((*T)->lchild), key)) return false; else { //刪除成功,修改樹的高度。 (*T)->height = max(GetHeight((*T)->lchild), GetHeight((*T)->rchild)) + 1; //已在*T的左子樹刪除結點key,判斷是否需要進行旋轉以保持二叉平衡樹的特性。 if (-2 == GetHeight((*T)->lchild) - GetHeight((*T)->rchild)) { if (GetHeight((*T)->rchild->lchild) > GetHeight((*T)->rchild->rchild)) { RLRotate(T); } else { RRRotate(T); } } return true; } } //在右子樹中遞歸刪除。 else { if (!AVLDelete(&((*T)->rchild), key)) return false; else { //刪除成功,修改樹的高度。 (*T)->height = max(GetHeight((*T)->lchild), GetHeight((*T)->rchild)) + 1; //已在*T的右子樹刪除結點key,判斷是否需要進行旋轉以保持二叉平衡樹的特性。 if (2 == GetHeight((*T)->lchild) - GetHeight((*T)->rchild)) { if (GetHeight((*T)->lchild->lchild) > GetHeight((*T)->lchild->rchild)) { LLRotate(T); } else { LRRotate(T); } } return true; } } }
關鍵點:
1. 當待刪除結點*T既有左子樹又有右子樹且左子樹高度大於右子樹高度時,用結點*T的前驅結點pre替換*T,之后再刪除前驅結點pre;當右子樹高度大於左子樹高度時,用結點*T的后繼節點post替換結點*T,之后再刪除后繼結點post。這樣可以保證在刪除操作之后,樹在不進行旋轉操作的情況下仍為二叉平衡樹。
2. 在刪除前驅結點pre和后繼結點post時,使用AVLDelete(&((*T)->lchild), pre->data)和AVLDelete(&((*T)->rchild), post->data)。這樣可以保證被刪除結點pre和post的父節點直至根結點的結點高度都會被遞歸修正一次。結點高度的遞歸修正同插入操作。
5. 時間復雜度
在平衡樹上進行查找的過程和排序樹相同,因此在查找過程中和給定值進行比較的關鍵字個數不超過樹的深度。假設F(N)表示N層平衡二叉樹的最少結點個數,則F[1]=1,F[2]=2,F(N)=F(N-2)+F(N-1)+1。在平衡樹上進行查找的時間復雜度為O(logn)。
6. 完整源代碼

#ifndef AVL_H #define AVL_H #include <stdio.h> #include <stdlib.h> //內含max(a,b)宏,故不需自己編寫。 #include <stdbool.h> /* * 二叉樹的二叉鏈表存儲結構。 * 額外添加樹的高度,以判斷結點的平衡度。 */ typedef int TElemType; typedef struct BiNode { TElemType data; struct BiNode *lchild; struct BiNode *rchild; int height; }BiNode, *BiTree; //函數聲明 bool AVLInsert(BiTree *T, TElemType key); bool AVLDelete(BiTree *T, TElemType key); bool LLRotate(BiTree *T); bool LRRotate(BiTree *T); bool RLRotate(BiTree *T); bool RRRotate(BiTree *T); int GetHeight(BiTree T); #endif

/* * 實現二叉平衡樹的插入、刪除操作 */ #include "AVL.h" /* * 插入操作。 * 如果以*T為根結點的二叉平衡樹中已有結點key,插入失敗,函數返回FALSE; * 否則將結點key插入到樹中,插入結點后的樹仍然為二叉平衡樹,函數返回TRUE。 */ bool AVLInsert(BiTree *T, TElemType key) { BiTree t; //如果當前查找的根結點為空樹,表明查無此結點,故插入結點。 if (!*T) { t = (BiTree)malloc(sizeof(BiNode)); t->data = key; t->height = 1; t->lchild = NULL; t->rchild = NULL; *T = t; return true; } //已有此結點,不再插入。 else if (key == (*T)->data) { return false; } //在左子樹中遞歸插入。 else if (key < (*T)->data) { if (!AVLInsert(&((*T)->lchild), key)) return false; else { //插入成功,修改樹的高度。 (*T)->height = max(GetHeight((*T)->lchild), GetHeight((*T)->rchild)) + 1; //已在*T的左子樹插入結點key,判斷是否需要進行旋轉以保持二叉平衡樹的特性。 if (2 == GetHeight((*T)->lchild) - GetHeight((*T)->rchild)) { //在左子樹的左子樹中插入結點。 if (GetHeight((*T)->lchild->lchild) > GetHeight((*T)->lchild->rchild)) { LLRotate(T); } //在左子樹的右子樹中插入結點。 else { LRRotate(T); } } return true; } } //在右子樹中遞歸插入。 else // (key > (*T)->data) { if (!AVLInsert(&(*T)->rchild, key)) return false; else { //插入成功,修改樹的高度。 (*T)->height = max(GetHeight((*T)->lchild), GetHeight((*T)->rchild)) + 1; //已在*T的右子樹插入結點key,判斷是否需要進行旋轉以保持二叉平衡樹的特性。 if (-2 == GetHeight((*T)->lchild) - GetHeight((*T)->rchild)) { //在右子樹的左子樹中插入結點。 if (GetHeight((*T)->rchild->lchild) > GetHeight((*T)->rchild->rchild)) { RLRotate(T); } //在右子樹的右子樹中插入結點。 else { RRRotate(T); } } return true; } } } /* * 刪除操作。 * 如果以*T為根結點的樹中存在結點key,將結點刪除,函數返回TRUE, * 否則刪除失敗,函數返回FALSE。 */ bool AVLDelete(BiTree *T, TElemType key) { BiTree pre, post; //沒有找到該結點。 if (!*T) return false; //找到結點,將它刪除。 else if (key == (*T)->data) { //待刪除節點為葉子結點。 if (!(*T)->lchild && !(*T)->rchild) *T = NULL; //待刪除結點只有右孩子。 else if (!(*T)->lchild) *T = (*T)->rchild; //待刪除結點只有左孩子。 else if (!(*T)->rchild) *T = (*T)->lchild; //待刪除結點既有左孩子,又有右孩子。 else { //當待刪除結點*T左子樹的高度大於右子樹的高度時,用*T的前驅結點pre代替*T, //再將結點pre從樹中刪除。這樣可以保證刪除結點后的樹仍為二叉平衡樹。 if (GetHeight((*T)->lchild) > GetHeight((*T)->rchild)) { //尋找前驅結點pre。 pre = (*T)->lchild; while (pre->rchild) { pre = pre->rchild; } //用pre替換*T。 (*T)->data = pre->data; //刪除節點pre。 //雖然能夠確定pre所屬最小子樹的根結點為&pre, //但是不采用AVLDelete(&pre,pre->data)刪除pre,目的是方便遞歸更改節點的高度。 AVLDelete(&((*T)->lchild), pre->data); } //當待刪除結點*T左子樹的高度小於或者等於右子樹的高度時,用*T的后繼結點post代替*T, //再將結點post從樹中刪除。這樣可以保證刪除結點后的樹仍為二叉平衡樹。 else { //尋找后繼節點post。 post = (*T)->rchild; while (post->lchild) post = post->lchild; //用post替換*T。 (*T)->data = post->data; //刪除節點post。 //雖然能夠確定post所屬最小子樹的根結點為&post, //但是不采用AVLDelete(&post,post->data)刪除post,目的是方便遞歸更改節點的高度。 AVLDelete(&((*T)->rchild), post->data); } } return true; } //在左子樹中遞歸刪除。 else if (key < (*T)->data) { if (!AVLDelete(&((*T)->lchild), key)) return false; else { //刪除成功,修改樹的高度。 (*T)->height = max(GetHeight((*T)->lchild), GetHeight((*T)->rchild)) + 1; //已在*T的左子樹刪除結點key,判斷是否需要進行旋轉以保持二叉平衡樹的特性。 if (-2 == GetHeight((*T)->lchild) - GetHeight((*T)->rchild)) { if (GetHeight((*T)->lchild->lchild) > GetHeight((*T)->lchild->rchild)) { LLRotate(T); } else { LRRotate(T); } } return true; } } //在右子樹中遞歸刪除。 else { if (!AVLDelete(&((*T)->rchild), key)) return false; else { //刪除成功,修改樹的高度。 (*T)->height = max(GetHeight((*T)->lchild), GetHeight((*T)->rchild)) + 1; //已在*T的右子樹刪除結點key,判斷是否需要進行旋轉以保持二叉平衡樹的特性。 if (2 == GetHeight((*T)->lchild) - GetHeight((*T)->rchild)) { if (GetHeight((*T)->rchild->lchild) > GetHeight((*T)->rchild->rchild)) { RLRotate(T); } else { RRRotate(T); } } return true; } } } /* * 當T的左子樹的左子樹上的節點使得T的平衡度為2時,以T為中心進行右旋。 */ bool LLRotate(BiTree *T) { BiTree lc; lc = (*T)->lchild; (*T)->lchild = lc->rchild; lc->rchild = (*T); //注意要更新結點的高度。整個樹中只有*T的左子樹和lc的右子樹發生了變化,所以只需更改這兩棵樹的高度。 (*T)->height = max(GetHeight((*T)->lchild), GetHeight((*T)->rchild)) + 1; lc->height = max(GetHeight(lc->lchild), GetHeight(lc->rchild)) + 1; *T = lc; return true; } /* * 當T的左子樹的右子樹上的節點使得T的平衡度為2時, * 先以T的左子樹為中心進行左旋,再以T為中心進行右旋。 */ bool LRRotate(BiTree *T) { RRRotate(&((*T)->lchild)); LLRotate(T); return true; } /* * 當T的右子樹的左子樹上的節點使得T的平衡度為-2時, * 先以T的右子樹為中心進行右旋,再以T為中心進行左旋。 */ bool RLRotate(BiTree *T) { LLRotate(&((*T)->rchild)); RRRotate(T); return true; } /* * 當T的右子樹的右子樹上的節點使得T的平衡度為-2時,以T為中心進行左旋。 */ bool RRRotate(BiTree *T) { BiTree rc; rc = (*T)->rchild; (*T)->rchild = rc->lchild; rc->lchild = (*T); //注意要更新結點的高度。整個樹中只有*T的左子樹和lc的右子樹發生了變化,所以只需更改這兩棵樹的高度。 (*T)->height = max(GetHeight((*T)->lchild), GetHeight((*T)->rchild)) + 1; rc->height = max(GetHeight(rc->lchild), GetHeight(rc->rchild)) + 1; *T = rc; return true; } /* * 當T=NULL ,即樹為空樹時,無法通過T->height獲取樹的高度0,所以要額外編寫該函數。 */ int GetHeight(BiTree T) { if (T) return T->height; return 0; }

/* * 二叉平衡樹的插入和刪除操作 */ #define _CRT_SECURE_NO_WARNINGS #include "AVL.h" #define TOTAL 10 bool InOrderTraverse(BiTree T); bool print(BiTree *T); int main() { BiTree T = NULL; //注意一定要將T初始化為空樹。 int i, num; //插入操作。 printf("輸入Total個數以構建二叉平衡樹:\n"); for (i = 0; i < TOTAL; i++) { scanf("%d", &num); AVLInsert(&T, num); } //檢驗插入操作。 printf("樹結構為:\n"); print(&T); printf("中序遍歷該樹,檢驗是否為由小到大:\n"); InOrderTraverse(T); putchar('\n'); printf("樹的深度為:%d\n", T->height); //刪除操作。 printf("輸入待刪除的數:"); scanf("%d", &num); AVLDelete(&T, num); //檢驗刪除操作。 printf("刪除后,樹結構為:\n"); print(&T); printf("中序遍歷該樹,檢驗是否為由小到大:\n"); InOrderTraverse(T); putchar('\n'); printf("樹的深度為:%d\n", T->height); return 0; } /* * 中序遍歷輸出樹結構。 */ bool InOrderTraverse(BiTree T) { if (T) { InOrderTraverse(T->lchild); printf("%-3d ", T->data); InOrderTraverse(T->rchild); } return true; } /* * 輸出樹結構,且標明父子關系。 */ bool print(BiTree *T) { if (!*T) return false; else { //如果樹有左孩子或者右孩子,則輸出它的左孩子和右孩子。 //例:5有左孩子3和右孩子6,輸出形式為:5(3,6) //5只有左孩子3,輸出形式為:5(3, ) //5只有右孩子6,輸出形式為:5( ,6) if ((*T)->lchild || (*T)->rchild) { printf("%d(", (*T)->data); if ((*T)->lchild) { printf("%-3d,", (*T)->lchild->data); } else { printf(" ,"); } if ((*T)->rchild) { printf("%-3d)\n", (*T)->rchild->data); } else { printf(" )\n"); } } //遞歸輸出左孩子和右孩子。 print(&((*T)->lchild)); print(&((*T)->rchild)); return true; } }
7. 測試結果
參考: