數據結構與算法系列研究五——樹、二叉樹、三叉樹、平衡排序二叉樹AVL


樹、二叉樹、三叉樹、平衡排序二叉樹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;                      //棧的數據結構
View Code

算法描述:用遞歸方法進行先序,中序,后序遍歷都十分方便與簡單,因為利用了樹的左右結構。若樹空時什么也不做,否則,按照便利的次序依次遞歸訪問即可,如先序遍歷:

//先序遞歸遍歷
void PreOrderTraverse(BinaryTree tree) { if(tree!=NULL) { visit(tree); PreOrderTraverse(tree->LChild); PreOrderTraverse(tree->RChild); } }
View Code

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

/***一直向左走直至獲得最左邊的指針*************/ 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;     //棧空時退出
 } }
View Code

  對於非遞歸后序遍歷,根據其特點,先遍歷左子樹再遍歷右子樹,最后訪問根節點。則需用兩個指針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); } } }
View Code

   接下來是用隊列數據結構層次遍歷。其實就是迭代的過程,先訪問頭節點,然后進左兒子,右兒子。每次出隊列后都要訪問該節點,然后再看該節點是否有左右子樹若有則按照先左后右的順序進隊排在隊尾等待遍歷然后不斷地循環迭代直至隊為空則遍歷結束。很容易理解!
具體代碼如下:

//隊列進行的二叉樹層次遍歷
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);  //則先進左子樹,后進右子樹,以保證從左到右遍歷
 } }
View Code

   然后關於二叉樹的復制,先復制左子樹,然后復制右子樹,最后復制頭節點;在復制左右子樹的時候同時也是復制樹,故可用遞歸實現。同理,關於求葉子節點,求樹的深度也是如此用遞歸即可實現,復制樹的具體代碼如下:

/************后序遍歷復制一棵二叉樹*************/
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; }
View Code

   最后就是根據先序和中序序列建樹,有一定的難度需要用到遞歸和分治法的一些知識。首先可以證明用先序,中序遍歷序列是可以還原二叉樹的,因為根據先序序列可以很清楚的知道二叉樹的根節點就是第一個元素,然后以這個節點把中序序列分成兩半,在這個節點左邊的必是左子樹(因為是中序序列),而在其右邊的是右子樹,而左子樹右子樹有是一個樹,故可以在更小的范圍內找到左子樹的根節點,在以該節點為分界點,在更小的范圍內查找下去,右子樹也是如此。在遞歸的過程中要注意進行比較的兩個序列長度必然要相等,遞歸終止的條件就是左邊分到不能再比,右邊也向右不能再比為止。這樣也就在遞歸中建立了二叉樹。具體代碼如下:

//首先得到中序序列二分后左邊的長度
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); } }
View Code

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     
View Code

三、三叉樹——帶雙親指針的二叉樹非遞歸遍歷
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;//指向雙親節點
}
View Code

然后是三序遍歷,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 }
View Code

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 }            
View Code

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; } } }
View Code

 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 }
View Code

四、平衡排序二叉樹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 }  
View Code

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 }
View Code


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM