樹、二叉樹、三叉樹、平衡排序二叉樹AVL
一、樹的定義
樹是計算機算法最重要的非線性結構。樹中每個數據元素至多有一個直接前驅,但可以有多個直接后繼。樹是一種以分支關系定義的層次結構。
a.樹是n(≥0)結點組成的有限集合。{N.沃恩}
(樹是n(n≥1)個結點組成的有限集合。{D.E.Knuth})
在任意一棵非空樹中:
⑴有且僅有一個沒有前驅的結點----根(root)。
⑵當n>1時,其余結點有且僅有一個直接前驅。
⑶所有結點都可以有0個或多個后繼。
b. 樹是n(n≥0)個結點組成的有限集合。
在任意一棵非空樹中:
⑴有一個特定的稱為根(root)的結點。
⑵當n>1時,其余結點分為m(m≥0)個互不相交的子集T1,T2,…,Tm。 每個集合本身又是一棵樹,並且稱為根的子樹(subtree)
樹的固有特性---遞歸性。即非空樹是由若干棵子樹組成,而子樹又可以由若干棵更小的子樹組成。
樹的基本操作
1、InitTree(&T) 初始化
2、DestroyTree(&T) 撤消樹
3、CreatTree(&T,F) 按F的定義生成樹
4、ClearTree(&T) 清除
5、TreeEmpty(T) 判樹空
6、TreeDepth(T) 求樹的深度
7、Root(T) 返回根結點
8、Parent(T,x) 返回結點 x 的雙親
9、Child(T,x,i) 返回結點 x 的第i 個孩子
10、InsertChild(&T,&p,i,x) 把 x 插入到 P的第i棵子樹處
11、DeleteChild(&T,&p,i) 刪除結點P的第i棵子樹
12、traverse(T) 遍歷
樹的結點:包含一個數據元素及若干指向子樹的分支。
●結點的度: 結點擁有子樹的數目
●葉結點: 度為零的結點
●分枝結點: 度非零的結點
●樹的度: 樹中各結點度的最大值
●孩子: 樹中某個結點的子樹的根
●雙親: 結點的直接前驅
●兄弟: 同一雙親的孩子互稱兄弟
●祖先: 從根結點到某結點j 路徑上的所有結點(不包括指定結點)。
●子孫: 某結點的子樹中的任一結點稱為該結點的子孫
●結點層次: 從根結點到某結點 j 路徑上結點的數目(包括結點j)
●樹的深度: 樹中結點的最大層次
●有向樹:結點間的連線是有向的。我們所講的樹都是有向的。
●有序樹: 若樹中結點的各子樹從左到右是有次序的,稱該樹為有序樹,否則為無序樹
●森林: 由 m 棵互不相交的樹構成 F=(T1,T2,.......Tm)
一棵樹去掉根結點后就變成了森林。
二叉樹的性質
二叉樹的第i層結點數最多為2^(i-1)個(i>0)。
深度為K的二叉樹至多有(2^k)-1個結點(K>0)。
對任何一棵二叉樹,設n0,n1,n2分別是度為0,1,2的結點數,則有:n0=n2+1
證明:
∵ n= n0+ n1 + n2 (n為結點總數)
b= n1 +2 n2 (b 為分支總數)
b=n-1 (除根結點外,任一結點都有分支連入父結點)
∴ n=b+1= n1 +2 n2 +1= n0+ n1 + n2
整理得: n0 = n2 +1
具有n個結點的完全二叉樹高度為
具有n個結點的完全二叉樹具有如下特征:
① i=1 根結點,無雙親
i>1 其雙親結點為 (PARENT(i)= )
② 2i>n 結點i無左孩,否則 lchild(i)=2i
③ 2i+1>n 結點i無右孩,否則 rchild(i)=2i+1
二、二叉樹三序遍歷
2.1.實驗內容
1.用先序遞歸遍歷法建二叉樹
2.輸出三序遞歸遍歷與層次遍歷節點訪問次序,三序遍歷要求使用非遞歸和遞歸兩種!!!!!!
3.用先序,中序遍歷序列建二叉樹
4.后序遍歷復制一棵二叉樹,計算葉子個數和樹的深度!!!!
5.輸出后序遞歸遍歷及層次遍歷結果
2.2.輸入與輸出
輸入:輸入建立二叉樹的先序序列並且帶‘#’,用以建樹
輸出 :輸出四種遍歷的序列和用先序中序序列建成的二叉樹
2.3.關鍵數據結構與算法描述
關鍵數據結構:二叉樹的結構,節點,左孩子右孩子;棧的數據結構用以采用非遞歸方式遍歷二叉樹,循環隊列的數據結構用以層次遍歷二叉樹。
具體代碼如下:

1 typedef struct TreeNode 2 { 3 ElemType elem; 4 struct TreeNode *LChild,*RChild; 5 }BiTree,*BinaryTree; //二叉樹數據結構
6 typedef struct Queue 7 { 8 BinaryTree value[MAXSIZE]; 9 int front,rear; 10 }LinkQueue; //隊列數據結構
11 typedef BinaryTree ElemType1; //為BinaryTree起別名
12 typedef struct Stack 13 { 14 ElemType1 StackElem[MAXSIZE]; 15 int top; 16 }STACK; //棧的數據結構
算法描述:用遞歸方法進行先序,中序,后序遍歷都十分方便與簡單,因為利用了樹的左右結構。若樹空時什么也不做,否則,按照便利的次序依次遞歸訪問即可,如先序遍歷:

//先序遞歸遍歷
void PreOrderTraverse(BinaryTree tree) { if(tree!=NULL) { visit(tree); PreOrderTraverse(tree->LChild); PreOrderTraverse(tree->RChild); } }
而對於非遞歸的三序遍歷,就需要棧來做數據結構了,對於前序,中序遍歷來說,只需要從根節點開始,一直往左走,直至最左邊左子樹為空,並且在這個過程中,若是先序遍歷對於路上的節點都要訪問並且入棧直至訪問到到最左邊的節點,然后退棧訪問退棧節點的右子樹;若是中序遍歷則只需不斷的向左走並且入棧但不訪問,直至最左邊,然后訪問最左邊節點。然后退棧並訪問該節點,用同樣的方法訪問退棧節點的右子樹。最后若棧為空,則訪問完成。故此設立一個一直向左走訪問並且入棧的函數如下(中序與此類似,暫不贅述):

/***一直向左走直至獲得最左邊的指針*************/ BinaryTree GofarleftVisit(BinaryTree tree,STACK *s) { if(!tree) return NULL; //若無樹直接返回
BinaryTree p=tree; visit(p); //先訪問邏輯根節點
while(p->LChild) { push(s,p); //把訪問之后的入棧以便訪問右子樹
visit(p->LChild); //訪問左子樹
p=p->LChild; //不斷向左移動直至為空
} return p; } 非遞歸先序遍歷的算法為: //用非遞歸法先序訪問
void PreOrder(BinaryTree tree) { if(!tree) return ; STACK s; InitStack(&s); BinaryTree p; p=GofarleftVisit(tree,&s); //獲得最左指針
while(p) { if(p->RChild) p=GofarleftVisit(p->RChild,&s); //右邊繼續向左走
else
if(!IsEmptyStack(&s)) { pop(&s,p); } else p=NULL; //棧空時退出
} }
對於非遞歸后序遍歷,根據其特點,先遍歷左子樹再遍歷右子樹,最后訪問根節點。則需用兩個指針cur和pre來分別標記當前指針和前一個指針的位置。注意壓棧時先壓根節點,再壓右子樹,最后左子樹。若當前指針cur指向葉子節點則需訪問,或者pre指針不為空並且pre指向的節點恰是當前指針指向的左或右節點則代表pre子樹的下一層已經無樹,並且若等於左指針則右邊已無節點,(右邊若有則訪問完左邊之后必然先訪問右邊而不會跳到頭節點);若等於右節點,則左邊已訪問完或無左邊(因右邊先壓棧)。具體代碼如下:

//非遞歸后序遍歷
void postOrder(BinaryTree tree) { STACK s; InitStack(&s); BinaryTree cur,pre=0; push(&s,tree); /*****用兩個指針來判斷,如果為葉子節點或者左右子樹都訪問過就訪問該節點****/
while(!IsEmptyStack(&s)) { cur=gettop(&s); if((cur->LChild==NULL&&cur->RChild==NULL)|| (pre!=NULL&&(pre==cur->RChild||pre==cur->LChild))) { //注意pre只要與一個相等,若為左子樹則無右子樹; //若為右子樹則必然訪問過左子樹或無左子樹
visit(cur); //如果當前結點為葉子節點或者孩子節點都已被訪問就訪問
pop(&s,cur); pre=cur; //標記上次被訪問的節點
} else { if(cur->RChild!=NULL) push(&s,cur->RChild); //注意先把右子樹入棧再入左子樹,才能保持先訪問左子樹后訪問右子樹,后進先出!
if(cur->LChild!=NULL) push(&s,cur->LChild); } } }
接下來是用隊列數據結構層次遍歷。其實就是迭代的過程,先訪問頭節點,然后進左兒子,右兒子。每次出隊列后都要訪問該節點,然后再看該節點是否有左右子樹若有則按照先左后右的順序進隊排在隊尾等待遍歷然后不斷地循環迭代直至隊為空則遍歷結束。很容易理解!
具體代碼如下:

//隊列進行的二叉樹層次遍歷
void HierarchyBiTree(BinaryTree tree) { LinkQueue Q; //注意此處不能是指針
InitQueue(&Q); BinaryTree p=tree; if (tree==NULL) return ; visit(p); if (p->LChild) EnQueue(&Q,&p->LChild); //若指針不空則入隊列
if (p->RChild) EnQueue(&Q, &p->RChild); //若指針不空則入隊列
while (!IsEmpty(&Q)) { DeQueue(&Q, &p); //彈出指針進行訪問
visit(p); if (p->LChild) EnQueue(&Q, &p->LChild); //對指針所指的結構進行判斷若左右子樹不空
if (p->RChild) EnQueue(&Q, &p->RChild); //則先進左子樹,后進右子樹,以保證從左到右遍歷
} }
然后關於二叉樹的復制,先復制左子樹,然后復制右子樹,最后復制頭節點;在復制左右子樹的時候同時也是復制樹,故可用遞歸實現。同理,關於求葉子節點,求樹的深度也是如此用遞歸即可實現,復制樹的具體代碼如下:

/************后序遍歷復制一棵二叉樹*************/
void CopyBiTree(BinaryTree tree,BinaryTree &newtree) { BinaryTree lchild,rchild; if(!tree) { newtree=NULL; } if(tree->LChild)//若左子樹存在則遞歸復制
{ CopyBiTree(tree->LChild,lchild); } else { lchild=NULL; //否則為零
} if(tree->RChild) { CopyBiTree(tree->RChild,rchild); } else { rchild=NULL; } newtree=(BinaryTree)malloc(sizeof(BiTree)); newtree->elem=tree->elem; newtree->LChild=lchild; newtree->RChild=rchild; }
最后就是根據先序和中序序列建樹,有一定的難度需要用到遞歸和分治法的一些知識。首先可以證明用先序,中序遍歷序列是可以還原二叉樹的,因為根據先序序列可以很清楚的知道二叉樹的根節點就是第一個元素,然后以這個節點把中序序列分成兩半,在這個節點左邊的必是左子樹(因為是中序序列),而在其右邊的是右子樹,而左子樹右子樹有是一個樹,故可以在更小的范圍內找到左子樹的根節點,在以該節點為分界點,在更小的范圍內查找下去,右子樹也是如此。在遞歸的過程中要注意進行比較的兩個序列長度必然要相等,遞歸終止的條件就是左邊分到不能再比,右邊也向右不能再比為止。這樣也就在遞歸中建立了二叉樹。具體代碼如下:

//首先得到中序序列二分后左邊的長度
int get_left_len(int rootpos,int in_begin,int in_end,char * pre_order,char * in_order ) { for(int i = in_begin; i <= in_end; i++) { if(in_order[i] == pre_order[rootpos]) { return i-in_begin; //以雙親節點為分界點划分,返回左邊的真實長度
} } return -1; //若沒有則返回負值,用於判斷
} void creat(BinaryTree *pnode,int pre_begin,int pre_end,int in_begin,int in_end, char * pre_order,char * in_order) { *pnode =(BinaryTree)malloc(sizeof(BiTree)); //申請空間
BinaryTree temp = *pnode; //創建遍歷指針
temp->elem = pre_order[pre_begin]; //開始必為根節點
temp->LChild = NULL; //一定要初始化為0
temp->RChild = NULL; if(pre_begin == pre_end) { return ; //只有一個節點,則已創建完畢
} int left_len = get_left_len(pre_begin,in_begin,in_end,pre_order,in_order); if(left_len > 0) //若沒有會返回-1;若為0,則上面已創建;否則創建左子樹
{ creat(&temp->LChild,pre_begin+1,pre_begin+left_len, in_begin,in_begin+left_len-1,pre_order,in_order); } if(left_len < (in_end - in_begin)) //若left_len+inbegin>in_end-1則已經結束,否則創建右子樹
{ creat(&temp->RChild,pre_begin+left_len+1,pre_end, in_begin+left_len+1,in_end,pre_order,in_order); } }
2.4.測試與理論
具體的測試與理論見下圖
測試數據一:
先序遍歷:ABDFCEG
中序遍歷:DFBAECG
后序遍歷:FDBEGCA
輸入數據: ABD#F###CE##G##
對於測試先序和中序序列建樹的序列為
char * pre_order = "ABDEGJCFHIK";//先序序列
char * in_order = "DBGJEACFIKH"; //中序序列
輸出結果截圖:
測試數據二:
先序遍歷:ABDEGJCFHIK
中序遍歷:DBGJEACFIKH
后序遍歷:DJGEBKIHFCA
輸入序列:ABD##EG#J###C#F#HI#K###
輸出結果見下圖:
2.5.附錄

1 #include "stdio.h"
2 #include "stdlib.h"
3 #include "iostream"
4 using namespace std; 5 #define MAXSIZE 100
6 #define OK 1
7 #define NO 0
8 /**********************************************/
9 typedef int status; 10 typedef char ElemType; 11 typedef struct TreeNode 12 { 13 ElemType elem; 14 struct TreeNode *LChild,*RChild; 15 }BiTree,*BinaryTree; //二叉樹數據結構
16 typedef struct Queue 17 { 18 BinaryTree value[MAXSIZE]; 19 int front,rear; 20 }LinkQueue; //隊列數據結構
21 typedef BinaryTree ElemType1; //為BinaryTree起別名
22 typedef struct Stack 23 { 24 ElemType1 StackElem[MAXSIZE]; 25 int top; 26 }STACK; //棧的數據結構
27 /************************************************/
28 /*************以下是循環隊列的定義**************/
29 void InitQueue( LinkQueue *q) 30 { 31 q->front=-1; //注意初始化為-1
32 q->rear=-1; 33 } 34 status IsEmpty(LinkQueue *q) 35 { 36 if(q->rear==q->front) 37 return OK; //循環隊列開始為空或者運行時出隊的光標指到入隊的光標
38 else
39 return NO; 40 } 41 status IsFull(LinkQueue *q) 42 { 43 if(q->front==(q->rear+1)%MAXSIZE) 44 return OK; //隊滿的標志就是q->front指向啞元且啞元左邊為q->rear
45 else
46 return NO; 47 } 48 void EnQueue(LinkQueue *q, BinaryTree *tree) 49 { 50 if(IsFull(q)) 51 return ; //入隊操作,若隊滿則不能入隊
52 q->rear=(++(q->rear))%MAXSIZE; //注意一定要先自加,再賦值
53 q->value[q->rear]=*tree; 54 } 55 void DeQueue(LinkQueue *q, BinaryTree *tree) 56 { 57 if(IsEmpty(q)) 58 return ; 59 q->front=(++q->front)%MAXSIZE; 60 *tree=q->value[q->front]; //注意tree是指向指針的指針,不然將出錯
61 } 62 /**************************************************************/
63 /******************以下是棧的定義******************************/
64 void InitStack(STACK *s) 65 { 66 s->top=-1; //初始化
67 } 68 void push(STACK *s,ElemType1 e) 69 { 70 if(s->top>=MAXSIZE-1) 71 return ; 72 s->StackElem[++s->top]=e; //壓棧
73 } 74 void pop(STACK *s,ElemType1 &e) 75 { 76 if(s->top<=-1) 77 return ; 78 e=s->StackElem[s->top]; //出棧
79 s->top--; 80 } 81 ElemType1 gettop(STACK *s) 82 { 83 return s->StackElem[s->top]; //獲得棧頂元素
84 } 85 status IsEmptyStack(STACK *s) //判斷是否棧空
86 { 87 if(s->top==-1) 88 return OK; 89 else
90 return NO; 91 } 92 /******************************************************************/
93 /***************遞歸創建二叉樹,要求讀入先序序列和‘#’****************/
94 BinaryTree CreatTree(BinaryTree tree) 95 { 96 char ch; 97 if((ch=getchar())=='#') 98 tree=NULL; 99 else
100 { 101 tree=(BinaryTree)malloc(sizeof(BiTree)); 102 tree->elem=ch; 103 tree->LChild=CreatTree(tree->LChild); 104 tree->RChild=CreatTree(tree->RChild); 105 } 106 return tree; 107 } 108 //最簡單的訪問二叉樹
109 void visit(BinaryTree tree) 110 { 111 printf("%c ",tree->elem); 112 } 113 /**************以下是四種對二叉樹的遍歷方法***********************/
114 //先序遞歸遍歷
115 void PreOrderTraverse(BinaryTree tree) 116 { 117 if(tree!=NULL) 118 { 119 visit(tree); 120 PreOrderTraverse(tree->LChild); 121 PreOrderTraverse(tree->RChild); 122 } 123 } 124
125 /***一直向左走直至獲得最左邊的指針*************/
126 BinaryTree GofarleftVisit(BinaryTree tree,STACK *s) 127 { 128 if(!tree) 129 return NULL; //若無樹直接返回
130 BinaryTree p=tree; 131 visit(p); //先訪問邏輯根節點
132 while(p->LChild) 133 { 134 push(s,p); //把訪問之后的入棧以便訪問右子樹
135 visit(p->LChild); //訪問左子樹
136 p=p->LChild; //不斷向左移動直至為空
137 } 138 return p; 139 } 140 //用非遞歸法先序訪問
141 void PreOrder(BinaryTree tree) 142 { 143 if(!tree) 144 return ; 145 STACK s; 146 InitStack(&s); 147 BinaryTree p; 148 p=GofarleftVisit(tree,&s); //獲得最左指針
149 while(p) 150 { 151 if(p->RChild) 152 p=GofarleftVisit(p->RChild,&s); //右邊繼續向左走
153 else
154 if(!IsEmptyStack(&s)) 155 { 156 pop(&s,p); 157 } 158 else
159 p=NULL; //棧空時退出
160 } 161 } 162
163 //中序遞歸遍歷
164 void InOrderTraverse(BinaryTree tree) 165 { 166 if(tree!=NULL) 167 { 168 InOrderTraverse(tree->LChild); 169 visit(tree); 170 InOrderTraverse(tree->RChild); 171 } 172 } 173 //中序非遞歸遍歷二叉樹
174 BinaryTree gofarleft(BinaryTree tree,STACK *s) 175 { 176 if(!tree) 177 return NULL; 178 BinaryTree p=tree; 179 while(p->LChild) //一直向左走,不斷入棧
180 { 181 push(s,p); 182 p=p->LChild; 183 } 184 return p; 185 } 186 void InOrder(BinaryTree tree) 187 { 188 if(!tree) 189 return ; 190 STACK s; 191 InitStack(&s); 192 BinaryTree p; 193 p=gofarleft(tree,&s); 194 while(p) 195 { 196 visit(p); //先訪問最左元素
197 if(p->RChild) 198 p=gofarleft(p->RChild,&s); 199 else
200 if(!IsEmptyStack(&s)) 201 { 202 pop(&s,p); //向上追溯
203 } 204 else
205 p=NULL; //棧空時恰訪問完
206 } 207 } 208 /************************************/
209
210 //后序遞歸遍歷
211 void PostOrderTraverse(BinaryTree tree) 212 { 213 if(tree!=NULL) 214 { 215 PostOrderTraverse(tree->LChild); 216 PostOrderTraverse(tree->RChild); 217 visit(tree); 218 } 219 } 220 //非遞歸后序遍歷
221 void postOrder(BinaryTree tree) 222 { 223 STACK s; 224 InitStack(&s); 225 BinaryTree cur,pre=0; 226 push(&s,tree); 227 /*****用兩個指針來判斷,如果為葉子節點或者左右子樹都訪問過就訪問該節點****/
228 while(!IsEmptyStack(&s)) 229 { 230 cur=gettop(&s); 231 if((cur->LChild==NULL&&cur->RChild==NULL)||
232 (pre!=NULL&&(pre==cur->RChild||pre==cur->LChild))) 233 { //注意pre只要與一個相等,若為左子樹則無右子樹; 234 //若為右子樹則必然訪問過左子樹或無左子樹
235 visit(cur); //如果當前結點為葉子節點或者孩子節點都已被訪問就訪問
236 pop(&s,cur); 237 pre=cur; //標記上次被訪問的節點
238 } 239 else
240 { 241 if(cur->RChild!=NULL) 242 push(&s,cur->RChild); //注意先把右子樹入棧再入左子樹,才能保持先訪問左子樹后訪問右子樹
243 if(cur->LChild!=NULL) 244 push(&s,cur->LChild); 245 } 246 } 247 } 248 /******************************************************/
249 //隊列進行的二叉樹層次遍歷
250 void HierarchyBiTree(BinaryTree tree) 251 { 252 LinkQueue Q; //注意此處不能是指針
253 InitQueue(&Q); 254 BinaryTree p=tree; 255 if (tree==NULL) 256 return ; 257 visit(p); 258 if (p->LChild) 259 EnQueue(&Q,&p->LChild); //若指針不空則入隊列
260 if (p->RChild) 261 EnQueue(&Q, &p->RChild); //若指針不空則入隊列
262 while (!IsEmpty(&Q)) 263 { 264 DeQueue(&Q, &p); //彈出指針進行訪問
265 visit(p); 266 if (p->LChild) 267 EnQueue(&Q, &p->LChild); //對指針所指的結構進行判斷若左右子樹不空
268 if (p->RChild) 269 EnQueue(&Q, &p->RChild); //則先進左子樹,后進右子樹,以保證從左到右遍歷
270 } 271 } 272 /***************************************************/
273 /********計算葉子節點數*************/
274 void CountLeaf(BinaryTree tree,int &count) 275 { 276 if(tree) 277 { 278 if((tree->LChild==NULL)&&(tree->RChild==NULL)) 279 count++; 280 CountLeaf(tree->LChild,count); 281 CountLeaf(tree->RChild,count); 282 } 283 } 284 /************計算樹的深度**************/
285 int TreeDepth(BinaryTree tree) 286 { 287 int depth,ldepth,rdepth; 288 if(!tree) 289 depth=0; 290 else
291 { 292 ldepth=TreeDepth(tree->LChild); 293 rdepth=TreeDepth(tree->RChild); 294 depth=(ldepth>rdepth ? ldepth:rdepth)+1; 295 } 296 return depth; 297 } 298 /************后序遍歷復制一棵二叉樹*************/
299 void CopyBiTree(BinaryTree tree,BinaryTree &newtree) 300 { 301 BinaryTree lchild,rchild; 302 if(!tree) 303 { 304 newtree=NULL; 305 } 306 if(tree->LChild)//若左子樹存在則遞歸復制
307 { 308 CopyBiTree(tree->LChild,lchild); 309 } 310 else
311 { 312 lchild=NULL; //否則為零
313 } 314 if(tree->RChild) 315 { 316 CopyBiTree(tree->RChild,rchild); 317 } 318 else
319 { 320 rchild=NULL; 321 } 322 newtree=(BinaryTree)malloc(sizeof(BiTree)); 323 newtree->elem=tree->elem; 324 newtree->LChild=lchild; 325 newtree->RChild=rchild; 326 } 327 /*****************************************************/
328 /*************根據先序和中序序列建二叉樹*******************/
329 //首先得到中序序列二分后左邊的長度
330 int get_left_len(int rootpos,int in_begin,int in_end,char * pre_order,char * in_order ) 331 { 332 for(int i = in_begin; i <= in_end; i++) 333 { 334 if(in_order[i] == pre_order[rootpos]) 335 { 336 return i-in_begin; //以雙親節點為分界點划分,返回左邊的真實長度
337 } 338 } 339 return -1; //若沒有則返回負值,用於判斷
340 } 341
342 void creat(BinaryTree *pnode,int pre_begin,int pre_end,int in_begin,int in_end, 343 char * pre_order,char * in_order) 344 { 345 *pnode =(BinaryTree)malloc(sizeof(BiTree)); //申請空間
346 BinaryTree temp = *pnode; //創建遍歷指針
347 temp->elem = pre_order[pre_begin]; //開始必為根節點
348 temp->LChild = NULL; //一定要初始化為0
349 temp->RChild = NULL; 350 if(pre_begin == pre_end) 351 { 352 return ; //只有一個節點,則已創建完畢
353 } 354 int left_len = get_left_len(pre_begin,in_begin,in_end,pre_order,in_order); 355 if(left_len > 0) //若沒有會返回-1;若為0,則已創建;否則創建左子樹
356 { 357 creat(&temp->LChild,pre_begin+1,pre_begin+left_len, 358 in_begin,in_begin+left_len-1,pre_order,in_order); 359 } 360 if(left_len < (in_end - in_begin)) //若left_len+inbegin>in_end-1則已經結束,否則創建右子樹
361 { 362 creat(&temp->RChild,pre_begin+left_len+1,pre_end, 363 in_begin+left_len+1,in_end,pre_order,in_order); 364 } 365 } 366 /*********************************************************/
367 void MainMenu( ) 368 { 369 BinaryTree tree=0,newtree; 370 int count=0,depth; 371 /**********display***********/
372 tree=CreatTree(tree); 373 printf("前序遍歷:\n"); 374 PreOrderTraverse(tree); 375 printf("\n中序遍歷:\n"); 376 InOrderTraverse(tree); 377 printf("\n后序遍歷:\n"); 378 PostOrderTraverse(tree); 379 printf("\n層次遍歷二叉樹:\n"); 380 HierarchyBiTree(tree); 381 printf("\n非遞歸先序遍歷\n"); 382 PreOrder(tree); 383 printf("\n非遞歸中序遍歷\n"); 384 InOrder(tree); 385 printf("\n非遞歸后序遍歷\n"); 386 postOrder(tree); 387 /********algorithm************/
388 CountLeaf(tree,count); 389 printf("\n葉子個數為:%d\n",count); 390 depth=TreeDepth(tree); 391 printf("\n樹的深度為:%d\n",depth); 392 printf("\n復制二叉樹后的結果:\n"); 393 CopyBiTree(tree,newtree); 394 printf("\n先序遍歷:\n"); 395 PreOrderTraverse(newtree); 396 printf("\n中序遍歷:\n"); 397 InOrderTraverse(newtree); 398 printf("\n后序遍歷:\n"); 399 PostOrderTraverse(newtree); 400 printf("\n層次遍歷二叉樹:\n"); 401 HierarchyBiTree(newtree); 402 /*********用先序和中序建樹並輸出*************/
403 char * pre_order = "ABDEGJCFHIK";//先序序列
404 char * in_order = "DBGJEACFIKH"; //中序序列
405 BinaryTree root = NULL; 406 creat(&root,0,strlen(pre_order)-1,0,strlen(in_order)-1,pre_order,in_order); 407 printf("用先序和中序建樹后的結果:\n"); 408 printf("\n后序遍歷:\n"); 409 PostOrderTraverse(root); 410 printf("\n層次遍歷二叉樹:\n"); 411 HierarchyBiTree(root); 412 printf("\n操作結束\n"); 413 } 414
415 /* 測試數據 ABD#F###CE##G## */
416 /* 測試數據 ABD##EG#J###C#F#HI#K### */
417 int main() 418 { 419 MainMenu(); 420 return 0; 421 } 422
三、三叉樹——帶雙親指針的二叉樹非遞歸遍歷
3.1.實驗內容
建立三叉鏈表存儲結構,實現三序非遞歸遍歷。
3.2.輸入與輸出
輸入:帶有“#”的二叉樹字符串。
輸出:三序遍歷的結果。
3.3.關鍵數據結構與算法描述
關鍵數據結構:
三叉鏈表的數據結構如下:
typedef char ElemType;
typedef struct TriTree
{
ElemType elem;
struct TriTree *lchild,*rchild,*parent;
}*TRITREE,TriTreeNode;
算法描述:
三叉鏈表的創建和二叉樹創建基本相同,只不過增加了雙親指針就要給其賦值,根節點的雙親為NULL,其他節點在先序遞歸遍歷建二叉樹的時候賦值,若該節點有左右子樹,則左右節點的雙親指針就指向該節點。具體代碼為:

if(tree->lchild) { tree->lchild->parent=tree;//指向雙親節點
} if(tree->rchild) { tree->rchild->parent=tree;//指向雙親節點
}
然后是三序遍歷,1.首先是先序非遞歸遍歷,先遍歷頭節點,再遍歷左子樹,然后是右子樹,注意到從此處的雙親指針的用法,可以回溯。因此,當樹不為空的時候,首先遍歷該節點,然后看是否有左子樹,若有則指向左子樹,若無左子樹,則看是否有右子樹,若有則指向右子樹。如果左右子樹都不存在,則是葉子節點需要回溯。選定一個“記錄指針p”,永遠指向遍歷指針剛剛走過的位置。而遍歷指針則回溯。若遍歷指針的左兒子為p,並且右兒子不空則遍歷右子樹;若回溯到根節點的雙親則結束。否則繼續回溯。直至兩種情況出現一種。一直循環就可實現先序遍歷。代碼如下:

1 //非遞歸先序遍歷三叉鏈表
2 void preorder(TRITREE tree) 3 { 4 TRITREE p; 5 while(tree) //有樹就循環
6 { 7 visit(tree); //訪問根節點
8 if(tree->lchild) 9 { 10 tree=tree->lchild ; //一定要先看左子樹再看右子樹
11 } 12 else if(tree->rchild ) 13 { 14 tree=tree->rchild ; //進入下一循環
15 } 16 else
17 while(1) 18 { 19 p=tree; 20 tree=tree->parent;//形成連接結構
21 if(!tree) 22 break; //若無樹則退出
23 if(tree->lchild == p&&tree->rchild ) 24 { 25 tree=tree->rchild ; //訪問完左子樹,訪問右子樹
26 break; 27 } 28
29 } 30 } 31 }
2.中序遍歷思想是先遍歷左子樹再遍歷頭節點,最后遍歷右子樹。因回溯時有左子樹和右子樹兩種情況,則用一個標量來記錄mark=0代表未遍歷左子樹,mark=1代表已遍歷左子樹。則當樹不空時開始循環,mark開始置為零。要是沒遍歷左子樹則要先遍歷左子樹。左子樹遍歷之后mark置為一。然后看以該節點為根的右子樹是否存在若存在則遍歷,若不存在則回溯,同樣設p跟隨遍歷節點,若是左邊回溯則遍歷該節點,若是右邊回溯則繼續向上推進,若推進到最上面則遍歷結束。具體代碼如下:

1 //非遞歸中序遍歷三叉鏈表
2 void inorder(TRITREE tree) 3 { 4 TRITREE p; 5 int mark=0;//表示左子樹未遍歷
6 while(tree) 7 { 8 if(mark==0) 9 { 10 if(tree->lchild) 11 { 12 tree=tree->lchild;//一直到最左邊的節點
13 } 14 else
15 { 16 mark=1; //然后標記左邊已遍歷,其實在下面遍歷
17 } 18 } 19 else
20 { 21 visit(tree); //遍歷標記節點
22 if(tree->rchild) 23 { 24 tree=tree->rchild;//若有右子樹,則移位
25 mark=0; //標記未遍歷,回到上步
26 } 27 else
28 { //若無右子樹,則回溯
29 while(1) 30 { 31 p=tree; 32 tree=tree->parent; 33 if(!tree) 34 break; 35 if(tree->lchild == p) 36 { 37 mark=1;//表示左孩子遍歷過
38 break; 39 } 40 } 41
42 } 43 } 44 } 45 }
3.后序遍歷需要設定一個標量flag分別表示(0):左子樹未遍歷(1):左子樹已遍歷,該遍歷右子樹;(2)右子樹已遍歷,應遍歷頭節點。則開始flag=0;開始遍歷遍歷完左子樹flag=1;開始遍歷右子樹,此時若右子樹還是棵樹則要flag=0;判斷這棵樹的左右子樹情況直至右邊也被遍歷則要遍歷頭節點置flag=2;開始訪問。訪問完后要進行回溯,若是從左邊過來的就要訪問右邊,若是從右邊過來的則要訪問該節點。一直循環就可得到結果,具體代碼如下:

//非遞歸后序遍歷三叉鏈表
void postorder(TRITREE tree) { int flag=0;//標志變量可取0,1,2三種狀態
TRITREE p; while(tree) { switch(flag) { case 0://左子樹未遍歷
if(tree->lchild) tree=tree->lchild; else flag=1; break; case 1://右子樹未遍歷
if(tree->rchild) { tree=tree->rchild; flag=0; //右子樹可能是一棵樹,重新遍歷樹的左孩子
} else { flag=2; //沒有右子樹則開始遍歷頭節點
} break; case 2: //開始遍歷頭節點
visit(tree); p=tree; tree=tree->parent; //回溯判斷
if(tree) { if(p==tree->lchild) { flag=1;//左孩子已遍歷,開始右子樹
} else { flag=2;//右孩子已遍歷,開始遍歷頭節點
} } break; } } }
3.4.測試與理論
測試數據:
先序遍歷:ABDFCEG
中序遍歷:DFBAECG
后序遍歷:FDBEGCA
輸入數據: ABD#F###CE##G##
結果見下圖顯示:
3.5.附錄

1 #include "stdio.h"
2 #include "iostream.h"
3 #include "stdlib.h"
4 typedef char ElemType; 5 typedef struct TriTree 6 { 7 ElemType elem; 8 struct TriTree *lchild,*rchild,*parent; 9 }*TRITREE,TriTreeNode; 10
11
12 //先序遍歷創建三叉鏈表
13 TRITREE CreatTree(TRITREE &tree) 14 { 15 char ch; 16 if((ch=getchar())=='#') 17 tree=NULL; 18 else
19 { 20 tree=(TRITREE)malloc(sizeof(TriTreeNode)); 21 tree->elem=ch; 22 tree->lchild=CreatTree(tree->lchild); 23 tree->rchild=CreatTree(tree->rchild); 24 //增加parent指針,若無左右孩子則不用賦值
25 if(tree->lchild) 26 { 27 tree->lchild->parent=tree;//指向雙親節點
28 } 29
30 if(tree->rchild) 31 { 32 tree->rchild->parent=tree;//指向雙親節點
33 } 34 } 35 return tree; 36 } 37 //最簡單的訪問二叉樹
38 void visit(TRITREE tree) 39 { 40 printf("%c ",tree->elem); 41
42 } 43 //非遞歸先序遍歷三叉鏈表
44 void preorder(TRITREE tree) 45 { 46 TRITREE p; 47 while(tree) //有樹就循環
48 { 49 visit(tree); //訪問根節點
50 if(tree->lchild) 51 { 52 tree=tree->lchild ; //一定要先看左子樹再看右子樹
53 } 54 else if(tree->rchild ) 55 { 56 tree=tree->rchild ; //進入下一循環
57 } 58 else
59 while(1) 60 { 61 p=tree; 62 tree=tree->parent;//形成連接結構
63 if(!tree) 64 break; //若無樹則退出
65 if(tree->lchild == p&&tree->rchild ) 66 { 67 tree=tree->rchild ; //訪問完左子樹,訪問右子樹
68 break; 69 } 70
71 } 72 } 73 } 74 //非遞歸中序遍歷三叉鏈表
75 void inorder(TRITREE tree) 76 { 77 TRITREE p; 78 int mark=0;//表示左子樹未遍歷
79 while(tree) 80 { 81 if(mark==0) 82 { 83 if(tree->lchild) 84 { 85 tree=tree->lchild;//一直到最左邊的節點
86 } 87 else
88 { 89 mark=1; //然后標記左邊已遍歷,其實在下面遍歷
90 } 91 } 92 else
93 { 94 visit(tree); //遍歷標記節點
95 if(tree->rchild) 96 { 97 tree=tree->rchild;//若有右子樹,則移位
98 mark=0; //標記未遍歷,回到上步
99 } 100 else
101 { //若無右子樹,則回溯
102 while(1) 103 { 104 p=tree; 105 tree=tree->parent; 106 if(!tree) 107 break; 108 if(tree->lchild == p) 109 { 110 mark=1;//表示左孩子遍歷過
111 break; 112 } 113 } 114
115 } 116 } 117 } 118 } 119
120 //非遞歸后序遍歷三叉鏈表
121 void postorder(TRITREE tree) 122 { 123 int flag=0;//標志變量可取0,1,2三種狀態
124 TRITREE p; 125 while(tree) 126 { 127 switch(flag) 128 { 129 case 0://左子樹未遍歷
130 if(tree->lchild) 131 tree=tree->lchild; 132 else
133 flag=1; 134 break; 135 case 1://右子樹未遍歷
136 if(tree->rchild) 137 { 138 tree=tree->rchild; 139 flag=0; //右子樹可能是一棵樹,重新遍歷樹的左孩子
140 } 141 else
142 { 143 flag=2; //沒有右子樹則開始遍歷頭節點
144 } 145 break; 146 case 2: //開始遍歷頭節點
147 visit(tree); 148 p=tree; 149 tree=tree->parent; //回溯判斷
150 if(tree) 151 { 152 if(p==tree->lchild) 153 { 154 flag=1;//左孩子已遍歷,開始右子樹
155 } 156 else
157 { 158 flag=2;//右孩子已遍歷,開始遍歷頭節點
159 } 160 } 161 break; 162 } 163 } 164 } 165
166 //abd#f###ce##g##
167 int main() 168 { 169 TRITREE tree; 170
171 tree=CreatTree(tree); 172 tree->parent=0; 173 cout<<endl<<"先序非遞歸遍歷三叉樹:"<<endl; 174 preorder(tree); 175 cout<<endl<<"中序非遞歸遍歷三叉樹:"<<endl; 176 inorder(tree); 177 cout<<endl<<"后序非遞歸遍歷三叉樹:"<<endl; 178 postorder(tree); 179 cout<<endl; 180 return 0; 181 }
四、平衡排序二叉樹AVL
4.1.實驗內容
建立排序平衡二叉樹。
4.2.輸入與輸出
輸入:輸入一組節點,從而建立平衡排序二叉樹。
輸出:輸出平衡二叉樹的先序,中序遍歷,以及每個節點的平衡因子,以便進行還原二叉樹的形狀,判斷算法是否正確。
4.3.關鍵數據結構與核心算法
關鍵數據結構:
因為是二叉樹,則要有基本的節點,左右孩子指針,因為要排序旋轉,則要知道平衡因子,注意此處可以根據需要來添加雙親指針,因為運用了引用,則省去了此處。因此數據結構為:
typedef struct BSTNode
{
int data;//信息
int bf;//平衡因子
struct BSTNode *lchild,*rchild; //平衡樹左右兒子指針
}BSTNode,*BSTree;//平衡二叉排序樹結構的定義
核心算法:
建立平衡排序二叉樹算是算法中比較復雜的一個了,但是找到核心之后,也就只是比較復雜一些罷了,多練習一下即可。那核心是什么呢?要回答這個問題,就要深刻理解“平衡”和“排序”兩個詞的含義。顧名思義,平衡二叉樹加上排序二叉樹即是所要建的樹。1.平衡二叉樹要求每一個節點的左子樹深度減去右子樹的深度的絕對值要小於1。2.排序二叉樹要求按照中序遍歷該二叉樹得到從小到大排序的序列。因此在每插入一個結點的時候都要判斷是否平衡,1.若平衡則按照排序二叉樹的方法插入之,然后刷新平衡因子即可。2.重要是不平衡的時候就要從最接近的不平衡的地方旋轉的讓它平衡,這樣繼續下去,不斷插入就可以得到排序平衡二叉樹。
下面主要解釋一下旋轉方法:旋轉共有4種方式,其實,最核心的只有兩種基本操作即是L_Rotate( )和R_Rotate( ),分別是左旋和右旋。
對於LL型,即是最先出錯的節點平衡因子是2,左兒子平衡因子是1,運用一次右旋操作再刷新平衡因子即可。根據鏡像對稱原則,RR型與此是對應的,如法炮制即可。
重要的並且復雜的是LR和RL操作,這兩個操作也是鏡像對稱的只講一個即可。就比如LR吧,最先出錯的節點的平衡因子是2,該節點的左孩子的平衡因子是-1.則要對左孩子為根的子樹進行左旋,然后對該最近節點進行右旋,刷新平衡因子即可。具體算法如下:

1 /**************************兩個基本操作*****************************/
2 void L_Rotate(BSTree &p) 3 { 4 //對以*p為根的二叉排序樹做左旋處理,處理之后p指向新的樹根結點 5 //和R_Rotate()鏡像對稱
6 BSTree rc; 7 rc = p->rchild; 8 p->rchild = rc->lchild; 9 rc->lchild = p; 10 p = rc; 11 } 12 void R_Rotate(BSTree &p) 13 { 14 //對以*p為根的二叉排序樹做右旋處理,處理之后p指向新的樹根結點 15 //注意此處引用的好處就是不用再出現雙親指針
16 BSTree lc; 17 lc=p->lchild; //指向B的位置
18 p->lchild=lc->rchild; //此處轉換仍保持中序遍歷不變性
19 lc->rchild=p; //更改a的指針位置
20 p=lc; //lc變成新的a
21 } 22 /**********************四個旋轉操作,每兩個在一起*****************/
23 //包含LL和LR
24 void LeftBalance(BSTree &T) 25 { //對已*T為根的二叉排序樹做左平衡旋轉處理
26 BSTree lc,rd; 27 lc = T->lchild; //lc調整左邊
28 switch(lc->bf) 29 { 30 case LH://若是左邊高則為LL型,只需旋轉即可
31 T->bf = lc->bf = EH; //旋轉后平衡因子均為0
32 R_Rotate(T); //LL型需要右旋轉
33 break; 34 case RH://若是右邊高,則為LR型,需分兩步調整
35 rd = lc->rchild; //找到不確定因子rd
36 switch(rd->bf)//對不確定因子進行討論
37 { 38 case LH://左邊高調整后
39 T->bf = RH;//根節點右邊變高
40 lc->bf = EH; //lc變平衡
41 break; 42 case EH://rd有左右節點
43 T->bf = lc->bf = EH; //調整后持平
44 break; 45 case RH://右邊高
46 T->bf = EH;//根節點平衡
47 lc->bf = LH;//lc節點變成左邊高
48 break; 49 } 50 rd->bf=EH; //調整后rd節點一定平衡,且變成新的頭節點
51 L_Rotate(T->lchild); //1.先左旋
52 R_Rotate(T); //2.再右旋
53 } 54 } 55 /*************右平衡操作,包含RR和RL*******************************/
56 void RightBalance(BSTree &T) 57 { 58 //對已*T為根的二叉排序樹做右平衡旋轉處理 59 //因為為LeftBalance(BSTree &T)的鏡像,注釋則省略
60 BSTree lc,rd; 61 lc = T->rchild; 62 switch(lc->bf) 63 { 64 case RH://右邊高,RR型
65 T->bf = lc->bf = EH; 66 L_Rotate(T); 67 break; 68 case LH://左邊高,RL型,分兩步旋轉
69 rd = lc->lchild; 70 switch(rd->bf) 71 { 72 case RH: 73 T->bf = LH; 74 lc->bf = EH; 75 break; 76 case LH: 77 T->bf = EH; 78 lc->bf = RH; 79 break; 80 case EH: 81 T->bf = lc->bf = EH; 82 break; 83 } 84 rd->bf = EH; 85 R_Rotate(T->rchild); //1.先右旋
86 L_Rotate(T); //2.再左旋
87 } 88 } 89 至於插入操作主要就是判斷樹是不是平衡,若不平衡是左邊還是右邊,對於添加的新節點改變了樹的平衡了沒有,改變了左邊的還是右邊的,然后進行相應的旋轉處理。具體算法如下: 90 int InsertAVL(BSTree &T, int key, bool &taller) 91 { 92 //若在平衡二叉排序樹中不存在與關鍵字key相等的結點,則將關鍵字插入樹中 93 //布爾變量taller表示樹是否“長高”
94 if(T==NULL) 95 { 96 T = (BSTree)malloc(sizeof(BSTNode)); 97 T->data = key; 98 T->bf = EH;//葉子結點其平衡因子肯定為0
99 T->lchild = T->rchild = NULL; 100 taller = 1;//樹長高了
101 } 102 else
103 { 104 if(key==T->data) 105 {//如果樹中已存放此關鍵字則不予插入
106 taller = 0; 107 return 0; 108 } 109 if(key<T->data) 110 {//關鍵字小於根結點則插入其左子樹中
111 if(!InsertAVL(T->lchild,key,taller)) 112 return 0; 113 if(taller) 114 {//如果樹長高了,判斷是否平衡
115 switch(T->bf) 116 { 117 case LH://若左邊高,這次又加上一個左邊的節點,則肯定變為2,即需要調整
118 LeftBalance(T);//不平衡時調用左平衡函數,使左子樹平衡
119 taller = 0; 120 break; 121 case EH://若相等,在左邊加一個節點,則變為左邊高
122 T->bf = LH; 123 taller = 1; //樹變高
124 break; 125 case RH://若右邊高,在左邊加一個節點,則持平
126 T->bf = EH; 127 taller = 0; 128 break; 129 } 130 } 131 } 132 else
133 {//插入右子樹中
134 if(!InsertAVL(T->rchild,key,taller)) 135 return 0; 136 if(taller) 137 { 138 switch(T->bf) 139 { 140 case LH://同理,本來左邊高,在右邊加一個節點則持平
141 T->bf = EH; 142 taller = 0; 143 break; 144 case EH://右邊變高
145 T->bf = RH; 146 taller = 1; 147 break; 148 case RH://右邊不平衡了,需要調整
149 RightBalance(T); 150 taller = 0; 151 break; 152 } 153 } 154 } 155 } 156 return 1; 157 }
4.4.理論與測試
理論:如圖,給定一個序列的節點數組,按照幾種變換規則得到了圖示的排序二叉樹,可以得到三序遍歷和平衡因子。(由於圖形比較多,暫時為手工畫圖)
該序列為:47, 63, 54,28,31,14,26,53,99,81
先序遍歷:31,26,14,28,54,47,53,81,63,99
中序遍歷:14,26,28,31,47,53,54,63,81,99
平衡因子:31和47的平衡因子為-1,其他的都為0
測試:運行程序之后輸出為:
由先序和中序序列可以還原出樹的原型,對照可知結果是正確的。
4.5.討論與體會
排序二叉樹的中序遍歷結果即為升序排列,但是運算速率不是最高的,為了尋找更好的方法,平衡排序二叉樹便誕生了。對於開創者而言這是令人稱贊的,但是對於后學者來說,在學習算法的核心思想的同時,更重要的是別人是怎樣想到的,當一個現實生活的需要放在眼前是,我們要有開創的眼光,具有創新能力,這點是非常重要的,因為對於應用上來說有了第一個其他的就不再令人驚奇了。同時,遞歸,引用,開關、選擇分支語句的運用也要引起注意。學習圖的最好方法,就是數形結合,一定要多畫圖。
4.6.附錄(源代碼)

1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<iostream>
4 using namespace std; 5 #define LH 1//左邊高
6 #define EH 0//一樣高
7 #define RH -1//右邊高
8 typedef struct BSTNode 9 { 10 int data;//信息
11 int bf;//平衡因子
12 struct BSTNode *lchild,*rchild; //平衡樹左右兒子指針
13 }BSTNode,*BSTree;//平衡二叉排序樹結構的定義
14
15 void R_Rotate(BSTree &p) 16 { 17 //對以*p為根的二叉排序樹做右旋處理,處理之后p指向新的樹根結點 18 //注意此處引用的好處就是不用再出現雙親指針
19 BSTree lc; 20 lc=p->lchild; //指向B的位置
21 p->lchild=lc->rchild; //此處轉換仍保持中序遍歷不變性
22 lc->rchild=p; //更改a的指針位置
23 p=lc; //lc變成新的a
24 } 25 void L_Rotate(BSTree &p) 26 { 27 //對以*p為根的二叉排序樹做左旋處理,處理之后p指向新的樹根結點 28 //和R_Rotate()鏡像對稱
29 BSTree rc; 30 rc = p->rchild; 31 p->rchild = rc->lchild; 32 rc->lchild = p; 33 p = rc; 34 } 35 //包含LL和LR
36 void LeftBalance(BSTree &T) 37 { //對已*T為根的二叉排序樹做左平衡旋轉處理
38 BSTree lc,rd; 39 lc = T->lchild; //lc調整左邊
40 switch(lc->bf) 41 { 42 case LH://若是左邊高則為LL型,只需旋轉即可
43 T->bf = lc->bf = EH; //旋轉后平衡因子均為0
44 R_Rotate(T); //LL型需要右旋轉
45 break; 46 case RH://若是右邊高,則為LR型,需分兩步調整
47 rd = lc->rchild; //找到不確定因子rd
48 switch(rd->bf)//對不確定因子進行討論
49 { 50 case LH://左邊高調整后
51 T->bf = RH;//根節點右邊變高
52 lc->bf = EH; //lc變平衡
53 break; 54 case EH://rd有左右節點
55 T->bf = lc->bf = EH; //調整后持平
56 break; 57 case RH://右邊高
58 T->bf = EH;//根節點平衡
59 lc->bf = LH;//lc節點變成左邊高
60 break; 61 } 62 rd->bf=EH; //調整后rd節點一定平衡,且變成新的頭節點
63 L_Rotate(T->lchild); //1.先左旋
64 R_Rotate(T); //2.再右旋
65 } 66 } 67 /*************右平衡操作,包含RR和RL*******************************/
68 void RightBalance(BSTree &T) 69 { 70 //對已*T為根的二叉排序樹做右平衡旋轉處理 71 //因為為LeftBalance(BSTree &T)的鏡像,注釋則省略
72 BSTree lc,rd; 73 lc = T->rchild; 74 switch(lc->bf) 75 { 76 case RH://右邊高,RR型
77 T->bf = lc->bf = EH; 78 L_Rotate(T); 79 break; 80 case LH://左邊高,RL型,分兩步旋轉
81 rd = lc->lchild; 82 switch(rd->bf) 83 { 84 case RH: 85 T->bf = LH; 86 lc->bf = EH; 87 break; 88 case LH: 89 T->bf = EH; 90 lc->bf = RH; 91 break; 92 case EH: 93 T->bf = lc->bf = EH; 94 break; 95 } 96 rd->bf = EH; 97 R_Rotate(T->rchild); //1.先右旋
98 L_Rotate(T); //2.再左旋
99 } 100 } 101
102 int InsertAVL(BSTree &T, int key, bool &taller) 103 { 104 //若在平衡二叉排序樹中不存在與關鍵字key相等的結點,則將關鍵字插入樹中 105 //布爾變量taller表示樹是否“長高”
106 if(T==NULL) 107 { 108 T = (BSTree)malloc(sizeof(BSTNode)); 109 T->data = key; 110 T->bf = EH;//葉子結點其平衡因子肯定為0
111 T->lchild = T->rchild = NULL; 112 taller = 1;//樹長高了
113 } 114 else
115 { 116 if(key==T->data) 117 {//如果樹中已存放此關鍵字則不予插入
118 taller = 0; 119 return 0; 120 } 121 if(key<T->data) 122 {//關鍵字小於根結點則插入其左子樹中
123 if(!InsertAVL(T->lchild,key,taller)) 124 return 0; 125 if(taller) 126 {//如果樹長高了,判斷是否平衡
127 switch(T->bf) 128 { 129 case LH://若左邊高,這次又加上一個左邊的節點,則肯定變為2,即需要調整
130 LeftBalance(T);//不平衡時調用左平衡函數,使左子樹平衡
131 taller = 0; 132 break; 133 case EH://若相等,在左邊加一個節點,則變為左邊高
134 T->bf = LH; 135 taller = 1; //樹變高
136 break; 137 case RH://若右邊高,在左邊加一個節點,則持平
138 T->bf = EH; 139 taller = 0; 140 break; 141 } 142 } 143 } 144 else
145 {//插入右子樹中
146 if(!InsertAVL(T->rchild,key,taller)) 147 return 0; 148 if(taller) 149 { 150 switch(T->bf) 151 { 152 case LH://同理,本來左邊高,在右邊加一個節點則持平
153 T->bf = EH; 154 taller = 0; 155 break; 156 case EH://右邊變高
157 T->bf = RH; 158 taller = 1; 159 break; 160 case RH://右邊不平衡了,需要調整
161 RightBalance(T); 162 taller = 0; 163 break; 164 } 165 } 166 } 167 } 168 return 1; 169 } 170 //訪問函數,輸出節點以及相應平衡因子
171 void VisitTree(BSTree &T) 172 { //輸出結點
173 if(T!=NULL) 174 { 175 printf("%d ",T->data); 176 printf("平衡因子: %d\n",T->bf); 177 } 178 } 179
180 void PreOrderTraverse(BSTree &T) 181 {//遞歸實現先序遍歷
182 if(T!=NULL) 183 VisitTree(T); 184 if(T->lchild) 185 PreOrderTraverse(T->lchild); 186 if(T->rchild) 187 PreOrderTraverse(T->rchild); 188 } 189
190 void InOrderTraverse(BSTree &T) 191 {//遞歸實現中序遍歷
192 if(T->lchild) 193 InOrderTraverse(T->lchild); 194 if(T!=NULL) 195 VisitTree(T); 196 if(T->rchild) 197 InOrderTraverse(T->rchild); 198 } 199 int main( ) 200 { 201 BSTree T; 202 bool taller=0; 203 int i; 204 T=NULL; 205 int a[50]={47,63,54,28,31,14,26,53,99,81}; 206 for(i=0;i<10;i++) 207 { 208 InsertAVL(T,a[i],taller); 209 } 210 printf("先序遍歷:\n"); 211 PreOrderTraverse(T); 212 printf("\n中序遍歷:\n"); 213 InOrderTraverse(T); 214 printf("\n"); 215 return 0; 216 }