二叉树

维基百科,自由的百科全书
跳转到: 导航, 搜索
一個簡單的二叉樹

計算機科學中,二叉樹是每個節點最多有兩個子樹有序樹。通常子樹被稱作「左子樹」(left subtree)和「右子樹」(right subtree)。二叉樹常被用於實現二叉查找樹二叉堆

二叉樹的每個結點至多只有二棵子樹(不存在度大於2的結點),二叉樹的子樹有左右之分,次序不能顛倒。二叉樹的第i層至多有2i − 1個結點;深度為k的二叉樹至多有2k − 1個結點;對任何一棵二叉樹T,如果其終端結點數為n0,度為2的結點數為n2,則n0 = n2 + 1

樹和二叉樹的三個主要差別:

  1. 樹的結點個數至少為1,而二叉樹的結點個數可以為0;
  2. 樹中結點的最大度數沒有限制,而二叉樹結點的最大度數為2;
  3. 樹的結點無左、右之分,而二叉樹的結點有左、右之分。

目录

[编辑] 圖論中的定義

二叉樹在圖論中是這樣定義的:二叉樹是一個連通的無環圖,並且每一個頂點的度不大於3。有根二叉樹還要滿足根結點的度不大於2。有了根結點之後,每個頂點定義了唯一的父結點,和最多2個子結點。然而,沒有足夠的信息來區分左結點和右結點。如果不考慮連通性,允許圖中有多個連通分量,這樣的結構叫做森林。

[编辑] 二叉樹(Binary Tree)的類型

二叉樹是一個有根,並且每個節點最多有2個子節點。非空的二元樹,若樹葉總數為 n0,分支度為2的總數為 n2,則 n0 = n2 + 1。

BinaryTree leaf.jpg

一棵深度為k,且有2k − 1個節點的二叉樹,稱為滿二叉樹(Full Binary Tree)。這種樹的特點是每一層上的節點數都是最大節點數。在一棵二叉樹中,除最後一層外,若其餘層都是滿的,並且最後一層或者是滿的,或者是在右邊缺少連續若干節點,則此二叉樹為完全二叉樹(Complete Binary Tree)。具有n個節點的完全二叉樹的深度為log2n + 1。深度為k的完全二叉樹,至少有2k − 1個節點,至多有2k − 1個節點。

FullBT CompleteBT.jpg

Complete Binary Tree Full Binary Tree
總節點k 2h − 1< k < 2h − 1 k = 2h − 1
樹高h h = log2k + 1 h = log2(k + 1)

[编辑] 存儲二叉樹的方法

編程語言中能用多種方法來構造二叉樹。在使用記錄的編程語言中,二叉樹通常用樹結點結構來存儲。有時也包含指向唯一的父節點的指針。如果一個結點的子結點個數小於2,一些子結點指針可能為空值,或者為特殊的哨兵結點。

二叉樹也可以用數組來存儲,而且如果這是完全二叉樹,這種方法不會浪費空間。用這種緊湊排列,如果一個結點的索引為i,它的子結點能在索引2i+1和2i+2找到,並且它的父節點(如果有)能在索引floor((i-1)/2)找到(假設根節點的索引為0)。這種方法更有利於緊湊存儲和更好的訪問的局部性,特別是在前序遍歷中。然而,它需要連續的存儲空間,這樣在存儲高度為hn個結點組成的一般普通樹時將會浪費很多空間。一種最極壞的情況下如果深度為h的二叉樹每個節點只有右孩子需要佔用2的h次冪減1,而實際卻只有h個結點,空間的浪費太大,這是順序存儲結構的一大缺點。

一個存儲在數組中的完全二叉樹

[编辑] 順序存儲表示

[编辑] 存儲結構

 /* 二叉樹的順序存儲表示 */
 #define MAX_TREE_SIZE 100 /* 二叉樹的最大結點數 */
 typedef TElemType SqBiTree[MAX_TREE_SIZE]; /* 0號單元存儲根結點 */
 
 typedef struct
 {
   int level,order; /* 結點的層,本層序號(按滿二叉樹計算) */
 }position;

[编辑] 基本操作

 /* 二叉树的顺序存储的基本操作(23个)*/
 #define ClearBiTree InitBiTree /* 在順序存儲結構中,兩函數完全一樣 */
 #define DestroyBiTree InitBiTree /* 在順序存儲結構中,兩函數完全一樣 */
 void InitBiTree(SqBiTree T)
 { /* 構造空二叉樹T。因為T是數組名,故不需要& */
   int i;
   for(i=0;i<MAX_TREE_SIZE;i++)
     T[i]=Nil; /* 初值為空(Nil在主程中定義) */
 }
 void CreateBiTree(SqBiTree T)
 { /* 按層序次序輸入二叉樹中結點的值(字符型或整型), 構造順序存儲的二叉樹T */
   int i=0;
 #if CHAR /* 結點類型為字符 */
   int l;
   char s[MAX_TREE_SIZE];
   InitBiTree(T); /* 構造空二叉樹T */
   printf("請按層序輸入結點的值(字符),空格表示空結點,結點數≦%d:\n",MAX_TREE_SIZE);
   gets(s); /* 輸入字符串 */
   l=strlen(s); /* 求字符串的長度 */
   for(;i<l;i++) /* 將字符串賦值給T */
     T[i]=s[i];
 #else /* 結點類型為整型 */
   InitBiTree(T); /* 構造空二叉樹T */
   printf("請按層序輸入結點的值(整型),0表示空結點,輸999結束。結點數≦%d:\n",MAX_TREE_SIZE);
   while(1)
   {
     scanf("%d",&T[i]);
     if(T[i]==999)
     {
       T[i]=Nil;
       break;
     }
     i++;
   }
 #endif
   for(i=1;i<MAX_TREE_SIZE;i++)
     if(T[(i+1)/2-1]==Nil&&T[i]!=Nil) /* 此非根結點(不空)無雙親 */
     {
       printf("出現無雙親的非根結點"form"\n",T[i]);
       exit(ERROR);
     }
 }
 Status BiTreeEmpty(SqBiTree T)
 { /* 初始條件:二叉樹T存在。操作結果:若T為空二叉樹,則返回TRUE,否則FALSE */
   if(T[0]==Nil) /* 根結點為空,則樹空 */
     return TRUE;
   else
     return FALSE;
 }
 int BiTreeDepth(SqBiTree T)
 { /* 初始條件:二叉樹T存在。操作結果:返回T的深度 */
   int i,j=-1;
   for(i=MAX_TREE_SIZE-1;i>=0;i--) /* 找到最後一個結點 */
     if(T[i]!=Nil)
       break;
   i++; /* 為了便於計算 */
   do
     j++;
   while(i>=pow(2,j));   /*pow是原型為double pow( double x, double y ),計算x的y次方,h = log<sub>2</sub>k + 1來計算二叉樹的深度*/
   return j;
 }
 Status Root(SqBiTree T,TElemType *e)
 { /* 初始條件:二叉樹T存在。操作結果:當T不空,用e返回T的根,返回OK;否則返回ERROR,e無定義 */
   if(BiTreeEmpty(T)) /* T空 */
     return ERROR;
   else
   {
     *e=T[0];
     return OK;
   }
 }
 TElemType Value(SqBiTree T,position e)
 { /* 初始條件:二叉樹T存在,e是T中某個結點(的位置) */
   /* 操作結果:返回處於位置e(層,本層序號)的結點的值 */
   return T[(int)pow(2,e.level-1)+e.order-2];
 }
 Status Assign(SqBiTree T,position e,TElemType value)
 { /* 初始條件:二叉樹T存在,e是T中某個結點(的位置) */
   /* 操作結果:給處於位置e(層,本層序號)的結點賦新值value */
   int i=(int)pow(2,e.level-1)+e.order-2; /* 將層、本層序號轉為矩陣的序號 */
   if(value!=Nil&&T[(i+1)/2-1]==Nil) /* 給葉子賦非空值但雙親為空 */
     return ERROR;
   else if(value==Nil&&(T[i*2+1]!=Nil||T[i*2+2]!=Nil)) /*  給雙親賦空值但有葉子(不空) */
     return ERROR;
   T[i]=value;
   return OK;
 }
 TElemType Parent(SqBiTree T,TElemType e)
 { /* 初始條件:二叉樹T存在,e是T中某個結點 */
   /* 操作結果:若e是T的非根結點,則返回它的雙親,否則返回"空" */
   int i;
   if(T[0]==Nil) /* 空樹 */
     return Nil;
   for(i=1;i<=MAX_TREE_SIZE-1;i++)
     if(T[i]==e) /* 找到e */
       return T[(i+1)/2-1];
   return Nil; /* 沒找到e */
 }
 TElemType LeftChild(SqBiTree T,TElemType e)
 { /* 初始條件:二叉樹T存在,e是T中某個結點。操作結果:返回e的左孩子。若e無左孩子,則返回"空" */
   int i;
   if(T[0]==Nil) /* 空樹 */
     return Nil;
   for(i=0;i<=MAX_TREE_SIZE-1;i++)
     if(T[i]==e) /* 找到e */
       return T[i*2+1];
   return Nil; /* 沒找到e */
 }
 TElemType RightChild(SqBiTree T,TElemType e)
 { /* 初始條件:二叉樹T存在,e是T中某個結點。操作結果:返回e的右孩子。若e無右孩子,則返回"空" */
   int i;
   if(T[0]==Nil) /* 空樹 */
     return Nil;
   for(i=0;i<=MAX_TREE_SIZE-1;i++)
     if(T[i]==e) /* 找到e */
       return T[i*2+2];
   return Nil; /* 沒找到e */
 }
 TElemType LeftSibling(SqBiTree T,TElemType e)
 { /* 初始條件:二叉樹T存在,e是T中某個結點 */
   /* 操作結果:返回e的左兄弟。若e是T的左孩子或無左兄弟,則返回"空" */
   int i;
   if(T[0]==Nil) /* 空樹 */
     return Nil;
   for(i=1;i<=MAX_TREE_SIZE-1;i++)
     if(T[i]==e&&i%2==0) /* 找到e且其序號為偶數(是右孩子) */
       return T[i-1];
   return Nil; /* 沒找到e */
 }
 TElemType RightSibling(SqBiTree T,TElemType e)
 { /* 初始條件:二叉樹T存在,e是T中某個結點 */
   /* 操作結果:返回e的右兄弟。若e是T的右孩子或無右兄弟,則返回"空" */
   int i;
   if(T[0]==Nil) /* 空樹 */
     return Nil;
   for(i=1;i<=MAX_TREE_SIZE-1;i++)
     if(T[i]==e&&i%2) /* 找到e且其序號為奇數(是左孩子) */
       return T[i+1];
   return Nil; /* 沒找到e */
 }
 void Move(SqBiTree q,int j,SqBiTree T,int i) /* InsertChild()用到。加 */
 { /* 把從q的j結點開始的子樹移為從T的i結點開始的子樹 */
   if(q[2*j+1]!=Nil) /* q的左子樹不空 */
     Move(q,(2*j+1),T,(2*i+1)); /* 把q的j結點的左子樹移為T的i結點的左子樹 */
   if(q[2*j+2]!=Nil) /* q的右子樹不空 */
     Move(q,(2*j+2),T,(2*i+2)); /* 把q的j結點的右子樹移為T的i結點的右子樹 */
   T[i]=q[j]; /* 把q的j結點移為T的i結點 */
   q[j]=Nil; /* 把q的j結點置空 */
 }
 void InsertChild(SqBiTree T,TElemType p,int LR,SqBiTree c)
 { /* 初始條件:二叉樹T存在,p是T中某個結點的值,LR為0或1,非空二叉樹c與T不相交且右子樹為空 */
   /* 操作結果: 根據LR為0或1,插入c為T中p結點的左或右子樹。p結點的原有左或右子樹則成為c的右子樹 */
   int j,k,i=0;
   for(j=0;j<(int)pow(2,BiTreeDepth(T))-1;j++) /* 查找p的序號 */
     if(T[j]==p) /* j為p的序號 */
       break;
   k=2*j+1+LR; /* k為p的左或右孩子的序號 */
   if(T[k]!=Nil) /* p原來的左或右孩子不空 */
     Move(T,k,T,2*k+2); /* 把從T的k結點開始的子樹移為從k結點的右子樹開始的子樹 */
   Move(c,i,T,k); /* 把從c的i結點開始的子樹移為從T的k結點開始的子樹 */
 }
 typedef int QElemType; /* 設隊列元素類型為整型(序號) */
 #include "c3-2.h" /* 鏈隊列 */
 #include "bo3-2.c" /* 鏈隊列的基本操作 */
 Status DeleteChild(SqBiTree T,position p,int LR)
 { /* 初始條件:二叉樹T存在,p指向T中某個結點,LR為1或0 */
   /* 操作結果:根據LR為1或0,刪除T中p所指結點的左或右子樹 */
   int i;
   Status k=OK; /* 隊列不空的標誌 */
   LinkQueue q;
   InitQueue(&q); /* 初始化隊列,用於存放待刪除的結點 */
   i=(int)pow(2,p.level-1)+p.order-2; /* 將層、本層序號轉為矩陣的序號 */
   if(T[i]==Nil) /* 此結點空 */
     return ERROR;
   i=i*2+1+LR; /* 待刪除子樹的根結點在矩陣中的序號 */
   while(k)
   {
     if(T[2*i+1]!=Nil) /* 左結點不空 */
       EnQueue(&q,2*i+1); /* 入隊左結點的序號 */
     if(T[2*i+2]!=Nil) /* 右結點不空 */
       EnQueue(&q,2*i+2); /* 入隊右結點的序號 */
     T[i]=Nil; /* 刪除此結點 */
     k=DeQueue(&q,&i); /* 隊列不空 */
   }
   return OK;
 }
 void(*VisitFunc)(TElemType); /* 函數變量 */
 void PreTraverse(SqBiTree T,int e)
 { /* PreOrderTraverse()調用 */
   VisitFunc(T[e]);
   if(T[2*e+1]!=Nil) /* 左子樹不空 */
     PreTraverse(T,2*e+1);
   if(T[2*e+2]!=Nil) /* 右子樹不空 */
     PreTraverse(T,2*e+2);
 }
 void PreOrderTraverse(SqBiTree T,void(*Visit)(TElemType))
 { /* 初始條件:二叉樹存在,Visit是對結點操作的應用函數 */
   /* 操作結果:先序遍歷T,對每個結點調用函數Visit一次且僅一次 */
   VisitFunc=Visit;
   if(!BiTreeEmpty(T)) /* 樹不空 */
     PreTraverse(T,0);
   printf("\n");
 }
 void InTraverse(SqBiTree T,int e)
 { /* InOrderTraverse()調用 */
   if(T[2*e+1]!=Nil) /* 左子樹不空 */
     InTraverse(T,2*e+1);
   VisitFunc(T[e]);
   if(T[2*e+2]!=Nil) /* 右子樹不空 */
     InTraverse(T,2*e+2);
 }
 void InOrderTraverse(SqBiTree T,void(*Visit)(TElemType))
 { /* 初始條件:二叉樹存在,Visit是對結點操作的應用函數 */
   /* 操作結果:中序遍歷T,對每個結點調用函數Visit一次且僅一次 */
   VisitFunc=Visit;
   if(!BiTreeEmpty(T)) /* 樹不空 */
     InTraverse(T,0);
   printf("\n");
 }
 void PostTraverse(SqBiTree T,int e)
 { /* PostOrderTraverse()調用 */
   if(T[2*e+1]!=Nil) /* 左子樹不空 */
     PostTraverse(T,2*e+1);
   if(T[2*e+2]!=Nil) /* 右子樹不空 */
     PostTraverse(T,2*e+2);
   VisitFunc(T[e]);
 }
 void PostOrderTraverse(SqBiTree T,void(*Visit)(TElemType))
 { /* 初始條件:二叉樹T存在,Visit是對結點操作的應用函數 */
   /* 操作結果:後序遍歷T,對每個結點調用函數Visit一次且僅一次 */
   VisitFunc=Visit;
   if(!BiTreeEmpty(T)) /* 樹不空 */
     PostTraverse(T,0);
   printf("\n");
 }
 void LevelOrderTraverse(SqBiTree T,void(*Visit)(TElemType))
 { /* 層序遍歷二叉樹 */
   int i=MAX_TREE_SIZE-1,j;
   while(T[i]==Nil)
     i--; /* 找到最後一個非空結點的序號 */
   for(j=0;j<=i;j++) /* 從根結點起,按層序遍歷二叉樹 */
     if(T[j]!=Nil)
       Visit(T[j]); /* 只遍歷非空的結點 */
   printf("\n");
 }
 void Print(SqBiTree T)
 { /* 逐層、按本層序號輸出二叉樹 */
   int j,k;
   position p;
   TElemType e;
   for(j=1;j<=BiTreeDepth(T);j++)
   {
     printf("第%d層: ",j);
     for(k=1;k<=pow(2,j-1);k++)
     {
       p.level=j;
       p.order=k;
       e=Value(T,p);
       if(e!=Nil)
         printf("%d:"form" ",k,e);
     }
     printf("\n");
   }
 }

[编辑] 二叉鏈表存儲表示

[编辑] 存儲結構

/* 二叉樹的二叉鏈表存儲表示 */
 typedef struct BiTNode
 {
   TElemType data;
   struct BiTNode *lchild,*rchild; /* 左右孩子指針 */
 }BiTNode,*BiTree;

Eclb.jpg

[编辑] 基本操作

 /* 二叉樹的二叉鏈表存儲的基本操作(22個) */
 #define ClearBiTree DestroyBiTree /* 清空二叉樹和銷毀二叉樹的操作一樣 */
 #include"func6-3.c"
 /* 包括InitBiTree()、DestroyBiTree()、PreOrderTraverse()和InOrderTraverse()4函數 */
 void CreateBiTree(BiTree *T)
 { /* 算法6.4:按先序次序輸入二叉樹中結點的值(可為字符型或整型,在主程中定義),*/
   /* 構造二叉鏈表表示的二叉樹T。變量Nil表示空(子)樹。有改動 */
   TElemType ch;
   scanf(form,&ch);
   if(ch==Nil) /* 空 */
     *T=NULL;
   else
   {
     *T=(BiTree)malloc(sizeof(BiTNode)); /* 生成根結點 */
     if(!*T)
       exit(OVERFLOW);
     (*T)->data=ch;
     CreateBiTree(&(*T)->lchild); /* 構造左子樹 */
     CreateBiTree(&(*T)->rchild); /* 構造右子樹 */
   }
 }
 Status BiTreeEmpty(BiTree T)
 { /* 初始條件:二叉樹T存在。操作結果:若T為空二叉樹,則返回TRUE,否則FALSE */
   if(T)
     return FALSE;
   else
     return TRUE;
 }
 int BiTreeDepth(BiTree T)
 { /* 初始條件:二叉樹T存在。操作結果:返回T的深度 */
   int i,j;
   if(T==NULL)  /*如果T=NULL,这样写便于理解,当然也可以写成if(!T)*/; 
     return 0; /* 空樹深度為0 */
   if(T->lchild)
     i=BiTreeDepth(T->lchild); /* i為左子樹的深度 */
   else
     i=0;
   if(T->rchild)
     j=BiTreeDepth(T->rchild); /* j為右子樹的深度 */
   else
     j=0;
   return i>j?i+1:j+1; /* T的深度為其左右子樹的深度中的大者+1 */
 }
 TElemType Root(BiTree T)
 { /* 初始條件:二叉樹T存在。操作結果:返回T的根 */
   if(BiTreeEmpty(T))
     return Nil;
   else
     return T->data;
 }
 TElemType Value(BiTree p)
 { /* 初始條件:二叉樹T存在,p指向T中某個結點。操作結果:返回p所指結點的值 */
   return p->data;
 }
 void Assign(BiTree p,TElemType value)
 { /* 給p所指結點賦值為value */
   p->data=value;
 }
 typedef BiTree QElemType; /* 設隊列元素為二叉樹的指針類型 */
 #include"c3-2.h" /* 鏈隊列 */
 #include"bo3-2.c" /* 鏈隊列的基本操作 */
 TElemType Parent(BiTree T,TElemType e)
 { /* 初始條件:二叉樹T存在,e是T中某個結點 */
   /* 操作結果:若e是T的非根結點,則返回它的雙親,否則返回"空"*/
   LinkQueue q;
   QElemType a;
   if(T) /* 非空樹 */
   {
     InitQueue(&q); /* 初始化隊列 */
     EnQueue(&q,T); /* 樹根指針入隊 */
     while(!QueueEmpty(q)) /* 隊不空 */
     {
       DeQueue(&q,&a); /* 出隊,隊列元素賦給a */
       if(a->lchild&&a->lchild->data==e||a->rchild&&a->rchild->data==e)
       /* 找到e(是其左或右孩子) */
         return a->data; /* 返回e的雙親的值 */
       else /* 沒找到e,則入隊其左右孩子指針(如果非空) */
       {
         if(a->lchild)
           EnQueue(&q,a->lchild);
         if(a->rchild)
           EnQueue(&q,a->rchild);
       }
     }
   }
   return Nil; /* 樹空或沒找到e */
 }
 BiTree Point(BiTree T,TElemType s)
 { /* 返回二叉樹T中指向元素值為s的結點的指針。另加 */
   LinkQueue q;
   QElemType a;
   if(T) /* 非空樹 */
   {
     InitQueue(&q); /* 初始化隊列 */
     EnQueue(&q,T); /* 根指針入隊 */
     while(!QueueEmpty(q)) /* 隊不空 */
     {
       DeQueue(&q,&a); /* 出隊,隊列元素賦給a */
       if(a->data==s)
         return a;
       if(a->lchild) /* 有左孩子 */
         EnQueue(&q,a->lchild); /* 入隊左孩子 */
       if(a->rchild) /* 有右孩子 */
         EnQueue(&q,a->rchild); /* 入隊右孩子 */
     }
   }
   return NULL;
 }
 TElemType LeftChild(BiTree T,TElemType e)
 { /* 初始條件:二叉樹T存在,e是T中某個結點。操作結果:返回e的左孩子。若e無左孩子,則返回"空" */
   BiTree a;
   if(T) /* 非空樹 */
   {
     a=Point(T,e); /* a是結點e的指針 */
     if(a&&a->lchild) /* T中存在結點e且e存在左孩子 */
       return a->lchild->data; /* 返回e的左孩子的值 */
   }
   return Nil; /* 其餘情況返回空 */
 }
 TElemType RightChild(BiTree T,TElemType e)
 { /* 初始條件:二叉樹T存在,e是T中某個結點。操作結果:返回e的右孩子。若e無右孩子,則返回"空" */
   BiTree a;
   if(T) /* 非空樹 */
   {
     a=Point(T,e); /* a是結點e的指針 */
     if(a&&a->rchild) /* T中存在結點e且e存在右孩子 */
       return a->rchild->data; /* 返回e的右孩子的值 */
   }
   return Nil; /* 其餘情況返回空 */
 }
 TElemType LeftSibling(BiTree T,TElemType e)
 { /* 初始條件:二叉樹T存在,e是T中某個結點 */
   /* 操作結果:返回e的左兄弟。若e是T的左孩子或無左兄弟,則返回"空"*/
   TElemType a;
   BiTree p;
   if(T) /* 非空樹 */
   {
     a=Parent(T,e); /* a為e的雙親 */
     if(a!=Nil) /* 找到e的雙親 */
     {
       p=Point(T,a); /* p為指向結點a的指針 */
       if(p->lchild&&p->rchild&&p->rchild->data==e) /* p存在左右孩子且右孩子是e */
         return p->lchild->data; /* 返回p的左孩子(e的左兄弟) */
     }
   }
   return Nil; /* 其餘情況返回空 */
 }
 TElemType RightSibling(BiTree T,TElemType e)
 { /* 初始條件:二叉樹T存在,e是T中某個結點 */
   /* 操作結果:返回e的右兄弟。若e是T的右孩子或無右兄弟,則返回"空"*/
   TElemType a;
   BiTree p;
   if(T) /* 非空樹 */
   {
     a=Parent(T,e); /* a為e的雙親 */
     if(a!=Nil) /* 找到e的雙親 */
     {
       p=Point(T,a); /* p為指向結點a的指針 */
       if(p->lchild&&p->rchild&&p->lchild->data==e) /* p存在左右孩子且左孩子是e */
         return p->rchild->data; /* 返回p的右孩子(e的右兄弟) */
     }
   }
   return Nil; /* 其餘情況返回空 */
 }
 Status InsertChild(BiTree p,int LR,BiTree c) /* 形參T無用 */
 { /* 初始條件:二叉樹T存在,p指向T中某個結點,LR為0或1,非空二叉樹c與T不相交且右子樹為空 */
   /* 操作結果:根據LR為0或1,插入c為T中p所指結點的左或右子樹。p所指結點的 */
   /*           原有左或右子樹則成為c的右子樹 */
   if(p) /* p不空 */
   {
     if(LR==0)
     {
       c->rchild=p->lchild;
       p->lchild=c;
     }
     else /* LR==1 */
     {
       c->rchild=p->rchild;
       p->rchild=c;
     }
     return OK;
   }
   return ERROR; /* p空 */
 }
 Status DeleteChild(BiTree p,int LR) /* 形參T無用 */
 { /* 初始條件:二叉樹T存在,p指向T中某個結點,LR為0或1 */
   /* 操作結果:根據LR為0或1,刪除T中p所指結點的左或右子樹 */
   if(p) /* p不空 */
   {
     if(LR==0) /* 刪除左子樹 */
       ClearBiTree(&p->lchild);
     else /* 刪除右子樹 */
       ClearBiTree(&p->rchild);
     return OK;
   }
   return ERROR; /* p空 */
 }
 typedef BiTree SElemType; /* 設棧元素為二叉樹的指針類型 */
 #include"c3-1.h" /* 順序棧 */
 #include"bo3-1.c" /* 順序棧的基本操作 */
 void InOrderTraverse1(BiTree T,void(*Visit)(TElemType))
 { /* 採用二叉鏈表存儲結構,Visit是對數據元素操作的應用函數。算法6.3,有改動 */
   /* 中序遍歷二叉樹T的非遞歸算法(利用棧),對每個數據元素調用函數Visit */
   SqStack S;
   InitStack(&S);
   while(T||!StackEmpty(S))
   {
     if(T)
     { /* 根指針進棧,遍歷左子樹 */
       Push(&S,T);
       T=T->lchild;
     }
     else
     { /* 根指針退棧,訪問根結點,遍歷右子樹 */
       Pop(&S,&T);
       Visit(T->data);
       T=T->rchild;
     }
   }
   printf("\n");
 }
 void InOrderTraverse2(BiTree T,void(*Visit)(TElemType))
 { /* 採用二叉鏈表存儲結構,Visit是對數據元素操作的應用函數。算法6.2,有改動 */
   /* 中序遍歷二叉樹T的非遞歸算法(利用棧),對每個數據元素調用函數Visit */
   SqStack S;
   BiTree p;
   InitStack(&S);
   Push(&S,T); /* 根指針進棧 */
   while(!StackEmpty(S))
   {
     while(GetTop(S,&p)&&p)
       Push(&S,p->lchild); /* 向左走到盡頭 */
     Pop(&S,&p); /* 空指針退棧 */
     if(!StackEmpty(S))
     { /* 訪問結點,向右一步 */
       Pop(&S,&p);
       Visit(p->data);
       Push(&S,p->rchild);
     }
   }
   printf("\n");
 }
 void PostOrderTraverse(BiTree T,void(*Visit)(TElemType))
 { /* 初始條件:二叉樹T存在,Visit是對結點操作的應用函數 */
   /* 操作結果:後序遞歸遍歷T,對每個結點調用函數Visit一次且僅一次 */
   if(T) /* T不空 */
   {
     PostOrderTraverse(T->lchild,Visit); /* 先後序遍歷左子樹 */
     PostOrderTraverse(T->rchild,Visit); /* 再後序遍歷右子樹 */
     Visit(T->data); /* 最後訪問根結點 */
   }
 }
 void LevelOrderTraverse(BiTree T,void(*Visit)(TElemType))
 { /* 初始條件:二叉樹T存在,Visit是對結點操作的應用函數 */
   /* 操作結果:層序遞歸遍歷T(利用隊列),對每個結點調用函數Visit一次且僅一次 */
   LinkQueue q;
   QElemType a;
   if(T)
   {
     InitQueue(&q); /* 初始化隊列q */
     EnQueue(&q,T); /* 根指針入隊 */
     while(!QueueEmpty(q)) /* 隊列不空 */
     {
       DeQueue(&q,&a); /* 出隊元素(指針),賦給a */
       Visit(a->data); /* 訪問a所指結點 */
       if(a->lchild!=NULL) /* a有左孩子 */
         EnQueue(&q,a->lchild); /* 入隊a的左孩子 */
       if(a->rchild!=NULL) /* a有右孩子 */
         EnQueue(&q,a->rchild); /* 入隊a的右孩子 */
     }
     printf("\n");
   }
 }

[编辑] 三叉鏈表存儲表示

[编辑] 存儲結構

 /* 二叉樹的三叉鏈表存儲表示 */
 typedef struct BiTPNode
 {
   TElemType data;
   struct BiTPNode *parent,*lchild,*rchild; /* 雙親、左右孩子指針 */
 }BiTPNode,*BiPTree;

3clb.jpg

[编辑] 基本操作

 /* 二叉樹的三叉鏈表存儲的基本操作(21個) */
 #define ClearBiTree DestroyBiTree /* 清空二叉樹和銷毀二叉樹的操作一樣 */
 void InitBiTree(BiPTree *T)
 { /* 操作結果:構造空二叉樹T */
   *T=NULL;
 }
 void DestroyBiTree(BiPTree *T)
 { /* 初始條件:二叉樹T存在。操作結果:銷毀二叉樹T */
   if(*T) /* 非空樹 */
   {
     if((*T)->lchild) /* 有左孩子 */
       DestroyBiTree(&(*T)->lchild); /* 銷毀左孩子子樹 */
     if((*T)->rchild) /* 有右孩子 */
       DestroyBiTree(&(*T)->rchild); /* 銷毀右孩子子樹 */
     free(*T); /* 釋放根結點 */
     *T=NULL; /* 空指針賦0 */
   }
 }
 void CreateBiTree(BiPTree *T)
 { /* 按先序次序輸入二叉樹中結點的值(可為字符型或整型,在主程中定義),*/
   /* 構造三叉鏈表表示的二叉樹T */
   TElemType ch;
   scanf(form,&ch);
   if(ch==Nil) /* 空 */
     *T=NULL;
   else
   {
     *T=(BiPTree)malloc(sizeof(BiTPNode)); /* 動態生成根結點 */
     if(!*T)
       exit(OVERFLOW);
     (*T)->data=ch; /* 給根結點賦值 */
     (*T)->parent=NULL; /* 根結點無雙親 */
     CreateBiTree(&(*T)->lchild); /* 構造左子樹 */
     if((*T)->lchild) /* 有左孩子 */
       (*T)->lchild->parent=*T; /* 給左孩子的雙親域賦值 */
     CreateBiTree(&(*T)->rchild); /* 構造右子樹 */
     if((*T)->rchild) /* 有右孩子 */
       (*T)->rchild->parent=*T; /* 給右孩子的雙親域賦值 */
   }
 }
 Status BiTreeEmpty(BiPTree T)
 { /* 初始條件:二叉樹T存在。操作結果:若T為空二叉樹,則返回TRUE,否則FALSE */
   if(T)
     return FALSE;
   else
     return TRUE;
 }
 int BiTreeDepth(BiPTree T)
 { /* 初始條件:二叉樹T存在。操作結果:返回T的深度 */
   int i,j;
   if(!T)
     return 0; /* 空樹深度為0 */
   if(T->lchild)
     i=BiTreeDepth(T->lchild); /* i為左子樹的深度 */
   else
     i=0;
   if(T->rchild)
     j=BiTreeDepth(T->rchild); /* j為右子樹的深度 */
   else
     j=0;
   return i>j?i+1:j+1; /* T的深度為其左右子樹的深度中的大者+1 */
 }
 TElemType Root(BiPTree T)
 { /* 初始條件:二叉樹T存在。操作結果:返回T的根 */
   if(T)
     return T->data;
   else
     return Nil;
 }
 TElemType Value(BiPTree p)
 { /* 初始條件:二叉樹T存在,p指向T中某個結點。操作結果:返回p所指結點的值 */
   return p->data;
 }
 void Assign(BiPTree p,TElemType value)
 { /* 給p所指結點賦值為value */
   p->data=value;
 }
 typedef BiPTree QElemType; /* 設隊列元素為二叉樹的指針類型 */
 #include"c3-2.h" /* 鏈隊列 */
 #include"bo3-2.c" /* 鏈隊列的基本操作 */
 BiPTree Point(BiPTree T,TElemType e)
 { /* 返回二叉樹T中指向元素值為e的結點的指針。加 */
   LinkQueue q;
   QElemType a;
   if(T) /* 非空樹 */
   {
     InitQueue(&q); /* 初始化隊列 */
     EnQueue(&q,T); /* 根結點入隊 */
     while(!QueueEmpty(q)) /* 隊不空 */
     {
       DeQueue(&q,&a); /* 出隊,隊列元素賦給a */
       if(a->data==e)
         return a;
       if(a->lchild) /* 有左孩子 */
         EnQueue(&q,a->lchild); /* 入隊左孩子 */
       if(a->rchild) /* 有右孩子 */
         EnQueue(&q,a->rchild); /* 入隊右孩子 */
     }
   }
   return NULL;
 }
 TElemType Parent(BiPTree T,TElemType e)
 { /* 初始條件:二叉樹T存在,e是T中某個結點 */
   /* 操作結果:若e是T的非根結點,則返回它的雙親,否則返回"空"*/
   BiPTree a;
   if(T) /* 非空樹 */
   {
     a=Point(T,e); /* a是結點e的指針 */
     if(a&&a!=T) /* T中存在結點e且e是非根結點 */
       return a->parent->data; /* 返回e的雙親的值 */
   }
   return Nil; /* 其餘情況返回空 */
 }
 TElemType LeftChild(BiPTree T,TElemType e)
 { /* 初始條件:二叉樹T存在,e是T中某個結點。操作結果:返回e的左孩子。若e無左孩子,則返回"空" */
   BiPTree a;
   if(T) /* 非空樹 */
   {
     a=Point(T,e); /* a是結點e的指針 */
     if(a&&a->lchild) /* T中存在結點e且e存在左孩子 */
       return a->lchild->data; /* 返回e的左孩子的值 */
   }
   return Nil; /* 其餘情況返回空 */
 }
 TElemType RightChild(BiPTree T,TElemType e)
 { /* 初始條件:二叉樹T存在,e是T中某個結點。操作結果:返回e的右孩子。若e無右孩子,則返回"空" */
   BiPTree a;
   if(T) /* 非空樹 */
   {
     a=Point(T,e); /* a是結點e的指針 */
     if(a&&a->rchild) /* T中存在結點e且e存在右孩子 */
       return a->rchild->data; /* 返回e的右孩子的值 */
   }
   return Nil; /* 其餘情況返回空 */
 }
 TElemType LeftSibling(BiPTree T,TElemType e)
 { /* 初始條件:二叉樹T存在,e是T中某個結點 */
   /* 操作結果:返回e的左兄弟。若e是T的左孩子或無左兄弟,則返回"空"*/
   BiPTree a;
   if(T) /* 非空樹 */
   {
     a=Point(T,e); /* a是結點e的指針 */
     if(a&&a!=T&&a->parent->lchild&&a->parent->lchild!=a) /* T中存在結點e且e存在左兄弟 */
       return a->parent->lchild->data; /* 返回e的左兄弟的值 */
   }
   return Nil; /* 其餘情況返回空 */
 }
 TElemType RightSibling(BiPTree T,TElemType e)
 { /* 初始條件:二叉樹T存在,e是T中某個結點 */
   /* 操作結果:返回e的右兄弟。若e是T的右孩子或無右兄弟,則返回"空"*/
   BiPTree a;
   if(T) /* 非空樹 */
   {
     a=Point(T,e); /* a是結點e的指針 */
     if(a&&a!=T&&a->parent->rchild&&a->parent->rchild!=a) /* T中存在結點e且e存在右兄弟 */
       return a->parent->rchild->data; /* 返回e的右兄弟的值 */
   }
   return Nil; /* 其餘情況返回空 */
 }
 Status InsertChild(BiPTree p,int LR,BiPTree c) /* 形參T無用 */
 { /* 初始條件:二叉樹T存在,p指向T中某個結點,LR為0或1,非空二叉樹c與T不相交且右子樹為空 */
   /* 操作結果:根據LR為0或1,插入c為T中p所指結點的左或右子樹。p所指結點 */
   /*           的原有左或右子樹則成為c的右子樹 */
   if(p) /* p不空 */
   {
     if(LR==0)
     {
       c->rchild=p->lchild;
       if(c->rchild) /* c有右孩子(p原有左孩子) */
         c->rchild->parent=c;
       p->lchild=c;
       c->parent=p;
     }
     else /* LR==1 */
     {
       c->rchild=p->rchild;
       if(c->rchild) /* c有右孩子(p原有右孩子) */
         c->rchild->parent=c;
       p->rchild=c;
       c->parent=p;
     }
     return OK;
   }
   return ERROR; /* p空 */
 }
 Status DeleteChild(BiPTree p,int LR) /* 形參T無用 */
 { /* 初始條件:二叉樹T存在,p指向T中某個結點,LR為0或1 */
   /* 操作結果:根據LR為0或1,刪除T中p所指結點的左或右子樹 */
   if(p) /* p不空 */
   {
     if(LR==0) /* 刪除左子樹 */
       ClearBiTree(&p->lchild);
     else /* 刪除右子樹 */
       ClearBiTree(&p->rchild);
     return OK;
   }
   return ERROR; /* p空 */
 }
 void PreOrderTraverse(BiPTree T,void(*Visit)(BiPTree))
 { /* 先序遞歸遍歷二叉樹T */
   if(T)
   {
     Visit(T); /* 先訪問根結點 */
     PreOrderTraverse(T->lchild,Visit); /* 再先序遍歷左子樹 */
     PreOrderTraverse(T->rchild,Visit); /* 最後先序遍歷右子樹 */
   }
 }
 void InOrderTraverse(BiPTree T,void(*Visit)(BiPTree))
 { /* 中序遞歸遍歷二叉樹T */
   if(T)
   {
     InOrderTraverse(T->lchild,Visit); /* 中序遍歷左子樹 */
     Visit(T); /* 再訪問根結點 */
     InOrderTraverse(T->rchild,Visit); /* 最後中序遍歷右子樹 */
   }
 }
 void PostOrderTraverse(BiPTree T,void(*Visit)(BiPTree))
 { /* 後序遞歸遍歷二叉樹T */
   if(T)
   {
     PostOrderTraverse(T->lchild,Visit); /* 後序遍歷左子樹 */
     PostOrderTraverse(T->rchild,Visit); /* 後序遍歷右子樹 */
     Visit(T); /* 最後訪問根結點 */
   }
 }
 void LevelOrderTraverse(BiPTree T,void(*Visit)(BiPTree))
 { /* 層序遍歷二叉樹T(利用隊列) */
   LinkQueue q;
   QElemType a;
   if(T)
   {
     InitQueue(&q);
     EnQueue(&q,T);
     while(!QueueEmpty(q))
     {
       DeQueue(&q,&a);
       Visit(a);
       if(a->lchild!=NULL)
         EnQueue(&q,a->lchild);
       if(a->rchild!=NULL)
         EnQueue(&q,a->rchild);
     }
   }
 }

[编辑] 訪問二叉樹的方法

主条目:樹的遍歷

我們經常希望訪問樹中的每一個結點並且查看它的值。有很多常見的順序來訪問所有的結點,而且每一種都有有用的性質。

[编辑] 前(先)序、中序、後序遍歷

遍歷二叉樹:L、D、R分別表示遍歷左子樹、訪問根結點和遍歷右子樹,則先(根)序遍歷二叉樹的順序是DLR,中(根)序遍歷二叉樹的順序是LDR,後(根)序遍歷二叉樹的順序是LRD。還有按層遍歷二叉樹。這些方法的時間複雜度都是O(n),n為結點個數。

如果T2是由有序樹T轉換而來的二叉樹,那麼T中結點的前序就是T2中結點的前序,T中結點的後序就是T2中結點的中序。任何一棵二叉樹的葉結點在先序、中序和後序遍歷中的相對次序不發改變。設n,m為一棵二叉樹上的兩個結點,在中序遍歷時,n在m前的條件是n在m的左方。前序序列和中序序列相同的二叉樹為空樹或任一結點均無左孩子的非空二叉樹;中序序列和後序序列相同的二叉樹為空樹或任一結點均無右孩子的非空二叉樹;前序序列和後序序列相同的二叉樹為空樹或僅有一個結點的二叉樹。

假設我們有一個包含值的value和指向兩個子結點的leftright的樹結點結構。我們可以寫出這樣的過程:

visit(node)
    print node.value
    if node.left  != null then visit(node.left)
    if node.right != null then visit(node.right)

這樣會用中序打印出樹中的值。在中序,每個結點在訪問它的子結點之前訪問。類似地,如果打印語句在最後,每個結點在訪問他的子節點之後訪問,樹中的值會用後序來打印。在這兩種情況中,左子樹中的值比右子樹中得值先打印。

visit(node)
    if node.left  != null then visit(node.left)
    print node.value
    if node.right != null then visit(node.right)

最後,上面的中序遍歷,每個結點在訪問左子樹和右子樹之間訪問。這在遍歷二叉搜索樹時很常用,因為它能用遞增的順序來遍歷所有的值。

為什麼呢?如果n是二叉搜索樹的結點,那麼n的左子樹的所有結點的值都比n的值要小,而且n的右子樹的所有節點的值都比n的值要大。因此,如果我們順序遍歷左子樹,然後訪問n,然後順序遍歷右子樹。我們就已經順序訪問了整個樹。


一個簡單的二叉樹 在這個二叉樹中,
  • 前序遍歷的結果:2, 7, 2, 6, 5, 11, 5, 9, 4
  • 後序遍歷的結果:2, 5, 11, 6, 7, 4, 9, 5, 2
  • 中序遍歷的結果:2, 7, 5, 6, 11, 2, 5, 4, 9


以上的遞歸算法使用與樹的高度成比例的棧空間。如果我們在每個結點中存儲指向父結點的指針,那樣可以使用迭代算法,只使用常數空間實現所有這些遍歷。然而,指向父結點的指針佔用更多的空間。這只在需要指向父節點的指針或棧空間有限時才使用。例如, 這是一個中序遍歷的迭代算法:

visit(root)
    prev    := null
    current := root
    next    := null
    
    while current != null
        if prev == current.parent
            prev := current
            next := current.left
        if next == null or prev == current.left
            print current.value
            prev := current
            next := current.right
        if next == null or prev == current.right
            prev := current
            next := current.parent
        current := next


Bitree.JPG 用二叉樹表示下述表達式:a+b*(c-d)-e/f
  • 先序遍歷的序列是:-+a*b-cd/ef
  • 中序遍歷的序列是:a+b*c-d-e/f
  • 後序遍歷的序列是:abcd-*+ef/-

[编辑] 深度優先遍歷

在深度優先順序中,我們希望從根結點訪問最遠的結點。和圖的深度優先搜索不同的是,不需記住訪問過的每一個結點,因為樹中不會有環。前序,中序和後序遍歷都是深度優先遍歷的特例。參見深度優先搜索

[编辑] 廣度優先遍歷

和深度優先遍歷不同,廣度優先遍歷會先訪問離根節點最近的節點。參見廣度優先搜索。 二叉樹的廣度優先遍歷又稱按層次遍歷。算法借助隊列實現。

[编辑] 將n叉樹轉換為二叉樹

一般有序樹和二叉樹之間有一一映射關係,能進行相互轉換。

n叉樹轉換為二叉樹的方法:二叉樹中結點x的左子結點為n叉樹中結點x的左子結點;二叉樹中結點x的右子結點為n叉樹中結點x的第一個右邊的同級結點y。

例如,在左邊的樹中,A有6個子結點{B,C,D,E,F,G}。它能被轉換成右邊的二叉樹。

將n叉樹轉換為二叉樹的例子


  • 將一棵樹轉換為二叉樹的方法:
  1. 在兄弟之間加一連線;
  2. 對每個結點,除了其左孩子外,去除其與其餘孩子之間的聯繫;
  3. 以樹的根結點為軸心,將整樹順時針轉45度。

[编辑] 存儲結構與基本操作

樹的二叉鏈表表示法(孩子兄弟表示法)是樹和二叉樹轉換的媒介。

[编辑] 樹的二叉鏈表存儲表示

 /* 樹的二叉鏈表(孩子—兄弟)存儲表示 */
 typedef struct CSNode
 {
   TElemType data;
   struct CSNode *firstchild,*nextsibling;
 }CSNode,*CSTree;

Hzxd.jpg

[编辑] 樹的二叉鏈表存儲的基本操作

 /* 樹的二叉鏈表(孩子—兄弟)存儲的基本操作(17個) */
 #define ClearTree DestroyTree /* 二者操作相同 */
 #include"func6-2.c" /* 包括PreOrderTraverse() */
 void InitTree(CSTree *T)
 { /* 操作結果:構造空樹T */
   *T=NULL;
 }
 void DestroyTree(CSTree *T)
 { /* 初始條件:樹T存在。操作結果:銷毀樹T */
   if(*T)
   {
     if((*T)->firstchild) /* T有長子 */
       DestroyTree(&(*T)->firstchild); /* 銷毀T的長子為根結點的子樹 */
     if((*T)->nextsibling) /* T有下一個兄弟 */
       DestroyTree(&(*T)->nextsibling); /* 銷毀T的下一個兄弟為根結點的子樹 */
     free(*T); /* 釋放根結點 */
     *T=NULL;
   }
 }
 typedef CSTree QElemType; /* 定義隊列元素類型 */
 #include"c3-2.h" /* 定義LinkQueue類型(鏈隊列) */
 #include"bo3-2.c" /* LinkQueue類型的基本操作 */
 void CreateTree(CSTree *T)
 { /* 構造樹T */
   char c[20]; /* 臨時存放孩子結點(設不超過20個)的值 */
   CSTree p,p1;
   LinkQueue q;
   int i,l;
   InitQueue(&q);
   printf("請輸入根結點(字符型,空格為空): ");
   scanf("%c%*c",&c[0]);
   if(c[0]!=Nil) /* 非空樹 */
   {
     *T=(CSTree)malloc(sizeof(CSNode)); /* 建立根結點 */
     (*T)->data=c[0];
     (*T)->nextsibling=NULL;
     EnQueue(&q,*T); /* 入隊根結點的指針 */
     while(!QueueEmpty(q)) /* 隊不空 */
     {
       DeQueue(&q,&p); /* 出隊一個結點的指針 */
       printf("請按長幼順序輸入結點%c的所有孩子: ",p->data);
       gets(c);
       l=strlen(c);
       if(l>0) /* 有孩子 */
       {
         p1=p->firstchild=(CSTree)malloc(sizeof(CSNode)); /* 建立長子結點 */
         p1->data=c[0];
         for(i=1;i<l;i++)
         {
           p1->nextsibling=(CSTree)malloc(sizeof(CSNode)); /* 建立下一個兄弟結點 */
           EnQueue(&q,p1); /* 入隊上一個結點 */
           p1=p1->nextsibling;
           p1->data=c[i];
         }
         p1->nextsibling=NULL;
         EnQueue(&q,p1); /* 入隊最後一個結點 */
       }
       else
         p->firstchild=NULL; /* 長子指針為空 */
     }
   }
   else
     *T=NULL; /* 空樹 */
 }
 Status TreeEmpty(CSTree T)
 { /* 初始條件:樹T存在。操作結果:若T為空樹,則返回TURE,否則返回FALSE */
   if(T) /* T不空 */
     return FALSE;
   else
     return TRUE;
 }
 int TreeDepth(CSTree T)
 { /* 初始條件:樹T存在。操作結果:返回T的深度 */
   CSTree p;
   int depth,max=0;
   if(!T) /* 樹空 */
     return 0;
   if(!T->firstchild) /* 樹無長子 */
     return 1;
   for(p=T->firstchild;p;p=p->nextsibling)
   { /* 求子樹深度的最大值 */
     depth=TreeDepth(p);
     if(depth>max)
       max=depth;
   }
   return max+1; /* 樹的深度=子樹深度最大值+1 */
 }
 TElemType Value(CSTree p)
 { /* 返回p所指結點的值 */
   return p->data;
 }
 TElemType Root(CSTree T)
 { /* 初始條件:樹T存在。操作結果:返回T的根 */
   if(T)
     return Value(T);
   else
     return Nil;
 }
 CSTree Point(CSTree T,TElemType s)
 { /* 返回二叉鏈表(孩子—兄弟)樹T中指向元素值為s的結點的指針。另加 */
   LinkQueue q;
   QElemType a;
   if(T) /* 非空樹 */
   {
     InitQueue(&q); /* 初始化隊列 */
     EnQueue(&q,T); /* 根結點入隊 */
     while(!QueueEmpty(q)) /* 隊不空 */
     {
       DeQueue(&q,&a); /* 出隊,隊列元素賦給a */
       if(a->data==s)
         return a;
       if(a->firstchild) /* 有長子 */
         EnQueue(&q,a->firstchild); /* 入隊長子 */
       if(a->nextsibling) /* 有下一個兄弟 */
         EnQueue(&q,a->nextsibling); /* 入隊下一個兄弟 */
     }
   }
   return NULL;
 }
 Status Assign(CSTree *T,TElemType cur_e,TElemType value)
 { /* 初始條件:樹T存在,cur_e是樹T中結點的值。操作結果:改cur_e為value */
   CSTree p;
   if(*T) /* 非空樹 */
   {
     p=Point(*T,cur_e); /* p為cur_e的指針 */
     if(p) /* 找到cur_e */
     {
       p->data=value; /* 賦新值 */
       return OK;
     }
   }
   return ERROR; /* 樹空或沒找到 */
 }
 TElemType Parent(CSTree T,TElemType cur_e)
 { /* 初始條件:樹T存在,cur_e是T中某個結點 */
   /* 操作結果:若cur_e是T的非根結點,則返回它的雙親,否則函數值為"空"*/
   CSTree p,t;
   LinkQueue q;
   InitQueue(&q);
   if(T) /* 樹非空 */
   {
     if(Value(T)==cur_e) /* 根結點值為cur_e */
       return Nil;
     EnQueue(&q,T); /* 根結點入隊 */
     while(!QueueEmpty(q))
     {
       DeQueue(&q,&p);
       if(p->firstchild) /* p有長子 */
       {
         if(p->firstchild->data==cur_e) /* 長子為cur_e */
           return Value(p); /* 返回雙親 */
         t=p; /* 雙親指針賦給t */
         p=p->firstchild; /* p指向長子 */
         EnQueue(&q,p); /* 入隊長子 */
         while(p->nextsibling) /* 有下一個兄弟 */
         {
           p=p->nextsibling; /* p指向下一個兄弟 */
         if(Value(p)==cur_e) /* 下一個兄弟為cur_e */
         return Value(t); /* 返回雙親 */
         EnQueue(&q,p); /* 入隊下一個兄弟 */
         }
       }
     }
   }
   return Nil; /* 樹空或沒找到cur_e */
 }
 TElemType LeftChild(CSTree T,TElemType cur_e)
 { /* 初始條件:樹T存在,cur_e是T中某個結點 */
   /* 操作結果:若cur_e是T的非葉子結點,則返回它的最左孩子,否則返回"空"*/
   CSTree f;
   f=Point(T,cur_e); /* f指向結點cur_e */
   if(f&&f->firstchild) /* 找到結點cur_e且結點cur_e有長子 */
     return f->firstchild->data;
   else
     return Nil;
 }
 TElemType RightSibling(CSTree T,TElemType cur_e)
 { /* 初始條件:樹T存在,cur_e是T中某個結點 */
   /* 操作結果:若cur_e有右兄弟,則返回它的右兄弟,否則返回"空"*/
   CSTree f;
   f=Point(T,cur_e); /* f指向結點cur_e */
   if(f&&f->nextsibling) /* 找到結點cur_e且結點cur_e有右兄弟 */
     return f->nextsibling->data;
   else
     return Nil; /* 樹空 */
 }
 Status InsertChild(CSTree *T,CSTree p,int i,CSTree c)
 { /* 初始條件:樹T存在,p指向T中某個結點,1≦i≦p所指結點的度+1,非空樹c與T不相交 */
   /* 操作結果:插入c為T中p結點的第i棵子樹 */
   /* 因為p所指結點的地址不會改變,故p不需是引用類型 */
   int j;
   if(*T) /* T不空 */
   {
     if(i==1) /* 插入c為p的長子 */
     {
       c->nextsibling=p->firstchild; /* p的原長子現是c的下一個兄弟(c本無兄弟) */
       p->firstchild=c;
     }
     else /* 找插入點 */
     {
       p=p->firstchild; /* 指向p的長子 */
       j=2;
       while(p&&i>j)
       {
         p=p->nextsibling;
         j++;
       }
       if(j==i) /* 找到插入位置 */
       {
         c->nextsibling=p->nextsibling;
         p->nextsibling=c;
       }
       else /* p原有孩子數小於i-1 */
         return ERROR;
     }
     return OK;
   }
   else /* T空 */
     return ERROR;
 }
 Status DeleteChild(CSTree *T,CSTree p,int i)
 { /* 初始條件:樹T存在,p指向T中某個結點,1≦i≦p所指結點的度 */
   /* 操作結果:刪除T中p所指結點的第i棵子樹 */
   /* 因為p所指結點的地址不會改變,故p不需是引用類型 */
   CSTree b;
   int j;
   if(*T) /* T不空 */
   {
     if(i==1) /* 刪除長子 */
     {
       b=p->firstchild;
       p->firstchild=b->nextsibling; /* p的原次子現是長子 */
       b->nextsibling=NULL;
       DestroyTree(&b);
     }
     else /* 刪除非長子 */
     {
       p=p->firstchild; /* p指向長子 */
       j=2;
       while(p&&i>j)
       {
         p=p->nextsibling;
         j++;
       }
       if(j==i) /* 找到第i棵子樹 */
       {
         b=p->nextsibling;
         p->nextsibling=b->nextsibling;
         b->nextsibling=NULL;
         DestroyTree(&b);
       }
       else /* p原有孩子數小於i */
         return ERROR;
     }
     return OK;
   }
   else
     return ERROR;
 }
 void PostOrderTraverse(CSTree T,void(*Visit)(TElemType))
 { /* 後根遍歷孩子—兄弟二叉鏈表結構的樹T */
   CSTree p;
   if(T)
   {
     if(T->firstchild) /* 有長子 */
     {
       PostOrderTraverse(T->firstchild,Visit); /* 後根遍歷長子子樹 */
       p=T->firstchild->nextsibling; /* p指向長子的下一個兄弟 */
       while(p)
       {
         PostOrderTraverse(p,Visit); /* 後根遍歷下一個兄弟子樹 */
         p=p->nextsibling; /* p指向再下一個兄弟 */
       }
     }
     Visit(Value(T)); /* 最後訪問根結點 */
   }
 }
 void LevelOrderTraverse(CSTree T,void(*Visit)(TElemType))
 { /* 層序遍歷孩子—兄弟二叉鏈表結構的樹T */
   CSTree p;
   LinkQueue q;
   InitQueue(&q);
   if(T)
   {
     Visit(Value(T)); /* 先訪問根結點 */
     EnQueue(&q,T); /* 入隊根結點的指針 */
     while(!QueueEmpty(q)) /* 隊不空 */
     {
       DeQueue(&q,&p); /* 出隊一個結點的指針 */
       if(p->firstchild) /* 有長子 */
       {
         p=p->firstchild;
         Visit(Value(p)); /* 訪問長子結點 */
         EnQueue(&q,p); /* 入隊長子結點的指針 */
         while(p->nextsibling) /* 有下一個兄弟 */
         {
           p=p->nextsibling;
           Visit(Value(p)); /* 訪問下一個兄弟 */
           EnQueue(&q,p); /* 入隊兄弟結點的指針 */
         }
       }
     }
   }
 }

[编辑] 線索二叉樹 (threaded binary tree)

線索二叉樹(保留遍歷時結點在任一序列的前驅和後繼的信息):若結點有左子樹,則其lchild域指示其左孩子,否則令lchild域指示其前驅;若結點有右子樹,則其rchild域指示其右孩子,否則令rchild指示其後繼。還需在結點結構中增加兩個標誌域LTag和RTag。LTag=0時,lchild域指示結點的左孩子,LTag=1時,lchild域指示結點的前驅;RTag=0時,rchild域指示結點的右孩子,RTag=1時,rchild域指示結點的後繼。以這種結點結構構成的二叉鏈表作為二叉樹的存儲結構,叫做線索鏈表,其中指向結點前驅和後繼的指針叫做線索,加上線索的二叉樹稱為線索二叉樹。對二叉樹以某種次序遍歷使其變為線索二叉樹的過程叫做線索化。若對二叉樹進行中序遍歷,則所得的線索二叉樹稱為中序線索二叉樹,線索鏈表稱為為中序線索鏈表。線索二叉樹是一種物理結構。 Tbt1.jpg

在中序線索樹找結點後繼的規律是:若其右標誌為1,則右鏈為線索,指示其後繼,否則遍歷其右子樹時訪問的第一個結點(右子樹最左下的結點)為其後繼;找結點前驅的規律是:若其左標誌為1,則左鏈為線索,指示其前驅,否則遍歷左子樹時最後訪問的一個結點(左子樹中最右下的結點)為其前驅。
在後序線索樹中找到結點的後繼分三種情況:

  1. 若結點是二叉樹的根,則其後繼為空;
  2. 若結點是其雙親的右孩子,或是其雙親的左孩子且其雙親沒有右子樹,則其後繼即為雙親結點;
  3. 若結點是其雙親的左孩子,且其雙親有右子樹,則其後繼為雙親右子樹上按後序遍歷列出的第一個結點。

[编辑] 二叉線索存儲表示

[编辑] 存儲結構

二叉樹的二叉線索存儲表示:在線索鏈表上添加一個頭結點,並令其lchild域的指針指向二叉樹的根結點,其rchild域的指針指向中序遍歷時訪問的最後一個結點。令二叉樹中序序列中的第一個結點的lchild域指針和最後一個結點的rchild域的指針均指向頭結點,這樣就建立了一個雙向線索鏈表

 /* 二叉樹的二叉線索存儲表示 */
 typedef enum{Link,Thread}PointerTag; /* Link(0):指針,Thread(1):線索 */
 typedef struct BiThrNode
 {
   TElemType data;
   struct BiThrNode *lchild,*rchild; /* 左右孩子指針 */
   PointerTag LTag,RTag; /* 左右標誌 */
 }BiThrNode,*BiThrTree;

[编辑] 基本操作

 /* 二叉樹的二叉線索存儲的基本操作 */
 void CreateBiThrTree(BiThrTree *T)
 { /* 按先序輸入線索二叉樹中結點的值,構造線索二叉樹T。0(整型)/空格(字符型)表示空結點 */
   TElemType ch;
   scanf(form,&ch);
   if(ch==Nil)
     *T=NULL;
   else
   {
     *T=(BiThrTree)malloc(sizeof(BiThrNode)); /* 生成根結點(先序) */
     if(!*T)
       exit(OVERFLOW);
     (*T)->data=ch; /* 給根結點賦植 */
     CreateBiThrTree(&(*T)->lchild); /* 遞歸構造左子樹 */
     if((*T)->lchild) /* 有左孩子 */
       (*T)->LTag=Link; /* 給左標誌賦值(指針) */
     CreateBiThrTree(&(*T)->rchild); /* 遞歸構造右子樹 */
     if((*T)->rchild) /* 有右孩子 */
       (*T)->RTag=Link; /* 給右標誌賦值(指針) */
   }
 }
 BiThrTree pre; /* 全局變量,始終指向剛剛訪問過的結點 */
 void InThreading(BiThrTree p)
 { /* 通過中序遍歷進行中序線索化,線索化之後pre指向最後一個結點。算法6.7 */
   if(p) /* 線索二叉樹不空 */
   {
     InThreading(p->lchild); /* 遞歸左子樹線索化 */
     if(!p->lchild) /* 沒有左孩子 */
     {
       p->LTag=Thread; /* 左標誌為線索(前驅) */
       p->lchild=pre; /* 左孩子指針指向前驅 */
     }
     if(!pre->rchild) /* 前驅沒有右孩子 */
     {
       pre->RTag=Thread; /* 前驅的右標誌為線索(後繼) */
       pre->rchild=p; /* 前驅右孩子指針指向其後繼(當前結點p) */
     }
     pre=p; /* 保持pre指向p的前驅 */
     InThreading(p->rchild); /* 遞歸右子樹線索化 */
   }
 }
 void InOrderThreading(BiThrTree *Thrt,BiThrTree T)
 { /* 中序遍歷二叉樹T,並將其中序線索化,Thrt指向頭結點。算法6.6 */
   *Thrt=(BiThrTree)malloc(sizeof(BiThrNode));
   if(!*Thrt) /* 生成頭結點不成功 */
     exit(OVERFLOW);
   (*Thrt)->LTag=Link; /* 建頭結點,左標誌為指針 */
   (*Thrt)->RTag=Thread; /* 右標誌為線索 */
   (*Thrt)->rchild=*Thrt; /* 右指針回指 */
   if(!T) /* 若二叉樹空,則左指針回指 */
     (*Thrt)->lchild=*Thrt;
   else
   {
     (*Thrt)->lchild=T; /* 頭結點的左指針指向根結點 */
     pre=*Thrt; /* pre(前驅)的初值指向頭結點 */
     InThreading(T); /* 中序遍歷進行中序線索化,pre指向中序遍歷的最後一個結點 */
     pre->rchild=*Thrt; /* 最後一個結點的右指針指向頭結點 */
     pre->RTag=Thread; /* 最後一個結點的右標誌為線索 */
     (*Thrt)->rchild=pre; /* 頭結點的右指針指向中序遍歷的最後一個結點 */
   }
 }
 void InOrderTraverse_Thr(BiThrTree T,void(*Visit)(TElemType))
 { /* 中序遍歷線索二叉樹T(頭結點)的非遞歸算法。算法6.5 */
   BiThrTree p;
   p=T->lchild; /* p指向根結點 */
   while(p!=T)
   { /* 空樹或遍歷結束時,p==T */
     while(p->LTag==Link) /* 由根結點一直找到二叉樹的最左結點 */
       p=p->lchild;
     Visit(p->data); /* 訪問此結點 */
     while(p->RTag==Thread&&p->rchild!=T) /* p->rchild是線索(後繼),且不是遍歷的最後一個結點 */
     {
       p=p->rchild;
       Visit(p->data); /* 訪問後繼結點 */
     }
     p=p->rchild; /* 若p->rchild不是線索(是右孩子),p指向右孩子,返回循環,*/
   }              /* 找這棵子樹中序遍歷的第1個結點 */
 }
 void PreThreading(BiThrTree p)
 { /* PreOrderThreading()調用的遞歸函數 */
   if(!pre->rchild) /* p的前驅沒有右孩子 */
   {
     pre->rchild=p; /* p前驅的後繼指向p */
     pre->RTag=Thread; /* pre的右孩子為線索 */
   }
   if(!p->lchild) /* p沒有左孩子 */
   {
     p->LTag=Thread; /* p的左孩子為線索 */
     p->lchild=pre; /* p的左孩子指向前驅 */
   }
   pre=p; /* 移動前驅 */
   if(p->LTag==Link) /* p有左孩子 */
     PreThreading(p->lchild); /* 對p的左孩子遞歸調用preThreading() */
   if(p->RTag==Link) /* p有右孩子 */
     PreThreading(p->rchild); /* 對p的右孩子遞歸調用preThreading() */
 }
 void PreOrderThreading(BiThrTree *Thrt,BiThrTree T)
 { /* 先序線索化二叉樹T,頭結點的右指針指向先序遍歷的最後1個結點 */
   *Thrt=(BiThrTree)malloc(sizeof(BiThrNode));
   if(!*Thrt) /* 生成頭結點 */
     exit(OVERFLOW);
   (*Thrt)->LTag=Link; /* 頭結點的左指針為孩子 */
   (*Thrt)->RTag=Thread; /* 頭結點的右指針為線索 */
   (*Thrt)->rchild=*Thrt; /* 頭結點的右指針指向自身 */
   if(!T) /* 空樹 */
     (*Thrt)->lchild=*Thrt; /* 頭結點的左指針也指向自身 */
   else
   { /* 非空樹 */
     (*Thrt)->lchild=T; /* 頭結點的左指針指向根結點 */
     pre=*Thrt; /* 前驅為頭結點 */
     PreThreading(T); /* 從頭結點開始先序遞歸線索化 */
     pre->rchild=*Thrt; /* 最後一個結點的後繼指向頭結點 */
     pre->RTag=Thread;
     (*Thrt)->rchild=pre; /* 頭結點的後繼指向最後一個結點 */
   }
 }
 void PreOrderTraverse_Thr(BiThrTree T,void(*Visit)(TElemType))
 { /* 先序遍歷線索二叉樹T(頭結點)的非遞歸算法 */
   BiThrTree p=T->lchild; /* p指向根結點 */
   while(p!=T) /* p沒指向頭結點(遍歷的最後1個結點的後繼指向頭結點) */
   {
     Visit(p->data); /* 訪問根結點 */
     if(p->LTag==Link) /* p有左孩子 */
       p=p->lchild; /* p指向左孩子(後繼) */
     else /* p無左孩子 */
       p=p->rchild; /* p指向右孩子或後繼 */
   }
 }
 void PostThreading(BiThrTree p)
 { /* PostOrderThreading()調用的遞歸函數 */
   if(p) /* p不空 */
   {
     PostThreading(p->lchild); /* 對p的左孩子遞歸調用PostThreading() */
     PostThreading(p->rchild); /* 對p的右孩子遞歸調用PostThreading() */
     if(!p->lchild) /* p沒有左孩子 */
     {
       p->LTag=Thread; /* p的左孩子為線索 */
       p->lchild=pre; /* p的左孩子指向前驅 */
     }
     if(!pre->rchild) /* p的前驅沒有右孩子 */
     {
       pre->RTag=Thread; /* p前驅的右孩子為線索 */
       pre->rchild=p; /* p前驅的後繼指向p */
     }
     pre=p; /* 移動前驅 */
   }
 }
 void PostOrderThreading(BiThrTree *Thrt,BiThrTree T)
 { /* 後序遞歸線索化二叉樹 */
   *Thrt=(BiThrTree)malloc(sizeof(BiThrNode));
   if(!*Thrt) /* 生成頭結點 */
     exit(OVERFLOW);
   (*Thrt)->LTag=Link; /* 頭結點的左指針為孩子 */
   (*Thrt)->RTag=Thread; /* 頭結點的右指針為線索 */
   if(!T) /* 空樹 */
     (*Thrt)->lchild=(*Thrt)->rchild=*Thrt; /* 頭結點的左右指針指向自身 */
   else
   { /* 非空樹 */
     (*Thrt)->lchild=(*Thrt)->rchild=T; /* 頭結點的左右指針指向根結點(最後一個結點) */
     pre=*Thrt; /* 前驅為頭結點 */
     PostThreading(T); /* 從頭結點開始後序遞歸線索化 */
     if(pre->RTag!=Link) /* 最後一個結點沒有右孩子 */
     {
       pre->rchild=*Thrt; /* 最後一個結點的後繼指向頭結點 */
       pre->RTag=Thread;
     }
   }
 }
 void DestroyBiTree(BiThrTree *T)
 { /* DestroyBiThrTree調用的遞歸函數,T指向根結點 */
   if(*T) /* 非空樹 */
   {
     if((*T)->LTag==0) /* 有左孩子 */
       DestroyBiTree(&(*T)->lchild); /* 銷毀左孩子子樹 */
     if((*T)->RTag==0) /* 有右孩子 */
       DestroyBiTree(&(*T)->rchild); /* 銷毀右孩子子樹 */
     free(*T); /* 釋放根結點 */
     T=NULL; /* 空指針賦0 */
   }
 }
 void DestroyBiThrTree(BiThrTree *Thrt)
 { /* 初始條件:線索二叉樹Thrt存在。操作結果:銷毀線索二叉樹Thrt */
   if(*Thrt) /* 頭結點存在 */
   {
     if((*Thrt)->lchild) /* 根結點存在 */
       DestroyBiTree(&(*Thrt)->lchild); /* 遞歸銷毀頭結點lchild所指二叉樹 */
     free(*Thrt); /* 釋放頭結點 */
     *Thrt=NULL; /* 線索二叉樹Thrt指針賦0 */
   }
 }
个人工具
名字空间
操作
导航
帮助
工具
其他语言