二叉樹

一棵有 9 個節點且深度為 3 的二元樹，其根節點的值為 2，它既不平衡亦未經過排序

在電腦科學中，二元樹（Binary tree）是每個節點最多隻有兩個分支（即不存在分支度大於 2 的節點）的樹結構。通常分支被稱作 “左子樹” 或 “右子樹”。二元樹的分支具有左右次序，不能隨意顛倒。

二元樹的第層至多擁有個節點；深度為的二元樹至多總共有個節點（定義根節點所在深度 ），而總計擁有節點數符合的，稱為「滿二元樹」；深度為有個節點的二元樹，當且僅當其中的每一節點，都可以和深度的滿二元樹，序號 1 到的節點一對一對應時，稱為完全二元樹。對任何一棵非空的二元樹，如果其葉片（終端節點）數為，分支度為 2 的節點數為，則。

與普通樹不同，普通樹的節點個數至少為 1，而二元樹的節點個數可以為 0；普通樹節點的最大分支度沒有限制，而二元樹節點的最大分支度為 2；普通樹的節點無左、右次序之分，而二元樹的節點有左、右次序之分。

二元樹通常作為資料結構應用，典型用法是對節點定義一個標記函式，將一些值與每個節點相關聯。這樣標記的二元樹就可以實現二元搜尋樹和二元堆積，並應用於高效率的搜尋和排序。

圖論中的定義

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

性質

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

特殊型別

3.1 滿二元樹

在一顆二元樹中，除了葉節點外，每個節點都有 2 個子節點。

3.2 完美二叉樹

一棵深度為 k，且有個節點的二元樹，稱為完美二元樹（Perfect Binary Tree）。這種樹的特點是其中所有內部節點都有兩個子節點，並且所有葉子都處於同一級別。

3.2.1 性質

對於一棵深度為 的完美二叉樹：

共有 個結點
結點個數一定為奇數
第 層有 個結點
有 個葉子

3.3 完全二叉樹

在一顆二元樹中，若除最後一層外的其餘層都是滿的，並且最後一層要麼是滿的，要麼在右邊缺少連續若干節點，則此二元樹為完全二元樹（Complete Binary Tree）。具有 n 個節點的完全二元樹的深度為。深度為 k 的完全二元樹，至少有個節點，至多有個節點。

儲存

在程式設計語言中能用多種方法來構造二元樹。

4.1 順序儲存表示

二元樹可以用陣列或連結串列來儲存，若是滿二元樹就能緊湊排列而不浪費空間。如果某個節點的索引為 i，（假設根節點的索引為 0）則在它左子節點的索引會是，以及右子節點會是；而它的父節點（如果有）索引則為。這種方法更有利於緊湊儲存和更好的訪問的區域性性，特別是在前序遍歷中。然而，它需要連續的儲存空間，這樣在儲存高度為 h 的 n 個節點所組成的一般樹時，將浪費很多空間。在最糟糕的情況下，如果深度為 h 的二元樹其每個節點都只有右孩子，則該儲存結構需要佔用的空間，實際上卻有 h 個節點，浪費了不少空間，是順序儲存結構的一大缺點。

4.1.1 儲存結構

/ 二叉樹的順序儲存表示 /

define MAXTREESIZE 100 / 二叉樹的最大節點數 /

typedef TElemType SqBiTreeMAXTREESIZE; / 0 號單元儲存根節點 /

typedef struct

{int level,order; / 即節點的層 (按滿二叉樹計算) /

} position;

4.1.2 基本操作

/ 二元樹的順序儲存的基本操作 (23 個)/

define ClearBiTree InitBiTree / 在順序儲存結構中，兩函式完全一樣 /
define DestroyBiTree InitBiTree / 在順序儲存結構中，兩函式完全一樣 /

void InitBiTree (SqBiTree T) －－－（SqBiTree & T）

{ / 構造空二元樹 T。因為 T 是陣列名稱，故不需要 & /

int i;

for (i=0;i

void CreateBiTree (SqBiTree T)

{/ 按層序次序輸入二叉樹中結點的值 (字元型或整型), 構造順序儲存的二叉樹 T /

int i=0;

if CHAR / 結點型別為字元 /

int l;

char sMAXTREESIZE;

InitBiTree (T); / 構造空二元樹 T /

printf ("請按層序輸入結點的值 (字元)，空格表示空結點，節點數≦% d:\n",MAXTREESIZE);

gets (s); / 輸入字串 /

l=strlen (s); / 求字串的長度 /

for (;i

int BiTreeDepth (SqBiTree T)

{ / 初始條件：二元樹 T 存在。操作結果：返回 T 的深度 /

int i,j=-1;

for (i=MAXTREESIZE-1;i>=0;i--) / 找到最後一個節點 /

if (Ti!=Nil)

break;

i++; / 為了便於計算 /

do

j++;

while (i>=pow (2,j)); /pow 是原型為 double pow (double x, double y), 計算 x 的 y 次方，h = log2k + 1 來計算二元樹的深度 /

return j;

}

Status Root (SqBiTree T,TElemType *e)

{ / 初始條件：二元樹 T 存在。操作結果：當 T 不空，用 e 返回 T 的根，返回 OK；否則返回 ERROR，e 無定義 /

if (BiTreeEmpty (T)) / T 空 /

return ERROR;

else

{

e=T0;

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&&(Ti*2+1!=Nil

Ti2+2!=Nil)) / 給雙親賦空值但有葉子 (不空) */

return ERROR;

Ti=value;

return OK;

}

TElemType Parent (SqBiTree T,TElemType e)

{ / 初始條件：二叉樹 T 存在，e 是 T 中某個結點 /

/ 操作結果：若 e 是 T 的非根結點，則返回它的雙親，否則返回＂空＂ /

int i;

if (T0==Nil) / 空樹 /

return Nil;

for (i=1;i

TElemType LeftChild (SqBiTree T,TElemType e)

{ / 初始條件：二叉樹 T 存在，e 是 T 中某個結點。操作結果：返回 e 的左孩子。若 e 無左孩子，則返回 "空" /

int i;

if (T0==Nil) / 空樹 /

return Nil;

for (i=0;i

TElemType RightChild (SqBiTree T,TElemType e)

{ / 初始條件：二叉樹 T 存在，e 是 T 中某個結點。操作結果：返回 e 的右孩子。若 e 無右孩子，則返回 "空" /

int i;

if (T0==Nil) / 空樹 /

return Nil;

for (i=0;i

TElemType LeftSibling (SqBiTree T,TElemType e)

{ / 初始條件：二叉樹 T 存在，e 是 T 中某個結點 /

/ 操作結果：返回 e 的左兄弟。若 e 是 T 的左孩子或無左兄弟，則返回＂空＂ /

int i;

if (T0==Nil) / 空樹 /

return Nil;

for (i=1;i

TElemType RightSibling (SqBiTree T,TElemType e)

{ / 初始條件：二叉樹 T 存在，e 是 T 中某個結點 /

/ 操作結果：返回 e 的右兄弟。若 e 是 T 的右孩子或無右兄弟，則返回＂空＂ /

int i;

if (T0==Nil) / 空樹 /

return Nil;

for (i=1;i

void Move (SqBiTree q,int j,SqBiTree T,int i) / InsertChild 用到。加 /

{ / 把從 q 的 j 結點開始的子樹移為從 T 的 i 結點開始的子樹 /

if (q2j+1!=Nil) / q 的左子樹不空 */

Move (q,(2j+1),T,(2i+1)); / 把 q 的 j 結點的左子樹移為 T 的 i 結點的左子樹 /

if (q2j+2!=Nil) / q 的右子樹不空 */

Move (q,(2j+2),T,(2i+2)); / 把 q 的 j 結點的右子樹移為 T 的 i 結點的右子樹 /

Ti=qj; / 把 q 的 j 結點移為 T 的 i 結點 /

qj=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

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 (Ti==Nil) / 此結點空 /

return ERROR;

i=i2+1+LR; / 待刪除子樹的根結點在矩陣中的序號 */

while (k)

{if (T2i+1!=Nil) / 左結點不空 */

EnQueue (&q,2i+1); / 入隊左結點的序號 */

if (T2i+2!=Nil) / 右結點不空 */

EnQueue (&q,2i+2); / 入隊右結點的序號 */

Ti=Nil; / 刪除此結點 /

k=DeQueue (&q,&i); / 佇列不空 /

}

return OK;

}

void (VisitFunc)(TElemType); / 函式變數 */

void PreTraverse (SqBiTree T,int e)

{/ PreOrderTraverse 呼叫 /

VisitFunc (Te);

if (T2e+1!=Nil) / 左子樹不空 */

PreTraverse (T,2*e+1);

if (T2e+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 (T2e+1!=Nil) / 左子樹不空 */

InTraverse (T,2*e+1);

VisitFunc (Te);

if (T2e+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 (T2e+1!=Nil) / 左子樹不空 */

PostTraverse (T,2*e+1);

if (T2e+2!=Nil) / 右子樹不空 */

PostTraverse (T,2*e+2);

VisitFunc (Te);

}

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=MAXTREESIZE-1,j;

while (Ti==Nil)

i--; / 找到最後一個非空結點的序號 /

for (j=0;j

void Print (SqBiTree T)

{ / 逐層、按本層序號輸出二叉樹 /

int j,k;

position p;

TElemType e;

for (j=1;j

4.2 二叉連結串列儲存表示

在使用記錄或記憶體位址指標的程式設計語言中，二叉樹通常用樹結點結構來儲存。有時也包含指向唯一的父節點的指標。如果一個結點的子結點個數小於 2，一些子結點指標可能為空值，或者為特殊的哨兵結點。

使用連結串列能避免順序儲存浪費空間的問題，演演算法和結構相對簡單，但使用二叉連結串列，由於缺乏父鏈的指引，在找回父節點時需要重新掃描樹得知父節點的節點位址。

4.2.1 儲存結構

/ 二叉樹的二叉連結串列儲存表示 /

typedef struct BiTNode

{

TElemType data;

struct BiTNode lchild,rchild; / 左右孩子指標 /

} BiTNode,*BiTree;

4.2.2 基本操作

/ 二叉樹的二叉連結串列儲存的基本操作 (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");

}

}

4.3 三叉連結串列儲存表示

改進於二叉連結串列，增加父節點的指引，能更好地實現節點間的訪問，不過演演算法相對複雜。

當二叉樹用三叉鏈表表示時，有 N 個結點，就會有 N+2 個空指標。

4.3.1 儲存結構

/ 二叉樹的三叉連結串列儲存表示 /

typedef struct BiTPNode

{

TElemType data;

struct BiTPNode parent,lchild,rchild; / 父、左右孩子指標 */

} BiTPNode,*BiPTree;

4.3.2 基本操作

/ 二叉樹的三叉連結串列儲存的基本操作 (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);

}

}

}

遍歷

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

5.1 深度優先遍歷

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

5.1.1 前（先）序、中序、後序遍歷

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

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

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

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，然後順序遍歷右子樹。我們就已經循序存取了整個樹。

後序遍歷虛擬碼如下：

visit (node)

if node.left

= null then visit (node.left)

if node.right != null then visit (node.right)

print node.value

以上的遞迴演演算法使用與樹的高度成比例的棧空間。如果我們在每個結點中儲存指向父結點的指標，那樣可以使用反覆運算演演算法，只使用常數空間實現所有這些遍歷。然而，指向父結點的指標佔用更多的空間。這只在需要指向父節點的指標或棧空間有限時才使用。例如，

這是一個中序遍歷的反覆運算演演算法：

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

5.2 廣度優先遍歷

和深度優先遍歷不同，廣度優先遍歷會先訪問離根節點最近的節點。二叉樹的廣度優先遍歷又稱按層次遍歷。演演算法藉助佇列實現。

轉換自多叉樹

一般有序樹可對映為二叉樹，但反之未必成立。

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

二叉樹當且僅當根節點沒有右子結點時可轉換為 n 叉樹。

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

將一棵樹轉換為二叉樹的方法：
在兄弟之間加一連線；
對每個結點，除了其左孩子外，去除其與其餘孩子之間的聯絡；
以樹的根結點為軸心，將整樹順時針轉 45 度。

6.1 樹的二叉連結串列標記法（孩子兄弟標記法）

樹的二叉連結串列標記法（孩子兄弟標記法）是樹和二叉樹轉換的媒介。

6.1.1 儲存結構

/ 樹的二叉連結串列 (孩子 — 兄弟) 儲存表示 /

typedef struct CSNode

{

TElemType data;

struct CSNode firstchild,nextsibling;

} CSNode,*CSTree;

6.1.2 基本操作

/ 樹的二叉連結串列 (孩子 — 兄弟) 儲存的基本操作 (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 c20; / 臨時存放孩子結點 (設不超過 20 個) 的值 /

CSTree p,p1;

LinkQueue q;

int i,l;

InitQueue (&q);

printf ("請輸入根結點 (字元型，空格為空):");

scanf ("% c%*c",&c0);

if (c0!=Nil) / 非空樹 /

{ T=(CSTree) malloc (sizeof (CSNode)); / 建立根結點 */

(*T)->data=c0;

(*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=c0;

for (i=1;inextsibling=(CSTree) malloc (sizeof (CSNode)); / 建立下一個兄弟結點 /

EnQueue (&q,p1); / 入隊上一個結點 /

p1=p1->nextsibling;

p1->data=ci;

}

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 存在，cure 是樹 T 中結點的值。操作結果：改 cure 為 value /

CSTree p;

if (T) / 非空樹 */

{p=Point (T,cure); / p 為 cure 的指標 */

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)==cure) / 根結點值為 cure /

return Nil;

EnQueue (&q,T); / 根結點入隊 /

while (!QueueEmpty (q))

{DeQueue (&q,&p);

if (p->firstchild) / p 有長子 /

{if (p->firstchild->data==cure) / 長子為 cure /

return Value (p); / 返回雙親 /

t=p; / 雙親指標賦給 t /

p=p->firstchild; / p 指向長子 /

EnQueue (&q,p); / 入隊長子 /

while (p->nextsibling) / 有下一個兄弟 /

{p=p->nextsibling; / p 指向下一個兄弟 /

	 if (Value (p)==cure) / 下一個兄弟為 cure /

	 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,cure); / f 指向結點 cure /

if (f&&f->firstchild) / 找到結點 cure 且結點 cure 有長子 /

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,cure); / f 指向結點 cure /

if (f&&f->nextsibling) / 找到結點 cure 且結點 cure 有右兄弟 /

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 域指示結點的後繼。以這種結點結構構成的二叉連結串列作為二叉樹的儲存結構，叫做線索連結串列，其中指向結點前驅和後繼的指標叫做線索，加上線索的二叉樹稱為線索二叉樹。對二叉樹以某種次序遍歷使其變為線索二叉樹的過程叫做線索化。若對二叉樹進行中序遍歷，則所得的線索二叉樹稱為中序線索二叉樹，線索連結串列稱為為中序線索連結串列。線索二叉樹是一種物理結構。

在中序線索樹找結點後繼的規律是：若其右標誌為 1，則右鏈為線索，指示其後繼，否則遍歷其右子樹時訪問的第一個結點（右子樹最左下的結點）為其後繼；找結點前驅的規律是：若其左標誌為 1，則左鏈為線索，指示其前驅，否則遍歷左子樹時最後訪問的一個結點（左子樹中最右下的結點）為其前驅。

在後序線索樹中找到結點的後繼分三種情況：

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

7.1 二叉線索儲存表示

7.1.1 儲存結構

二叉樹的二叉線索儲存表示：線上索連結串列上新增一個頭結點，並令其 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;

7.1.2 基本操作

/ 二叉樹的二叉線索儲存的基本操作 /

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 && !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 /

}

}