二叉树
二叉树
维基百科,自由的百科全书
在计算机科学中,二叉树是每个节点最多有两个子树的有序树。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树和二叉堆。
二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。二叉树的第i层至多有2i − 1个结点;深度为k的二叉树至多有2k − 1个结点;对任何一棵二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0 = n2 + 1。
树和二叉树的三个主要差别:
- 树的结点个数至少为1,而二叉树的结点个数可以为0;
- 树中结点的最大度数没有限制,而二叉树结点的最大度数为2;
- 树的结点无左、右之分,而二叉树的结点有左、右之分。
一个简单的二叉树
目录
[隐藏][编辑]图论中的定义
二叉树在图论中是这样定义的:二叉树是一个连通的无环图,并且每一个顶点的度不大于2。有根二叉树还要满足根结点的度不大于2。有了根结点之后,每个顶点定义了唯一的父结点,和最多2个子结点。然而,没有足够的信息来区分左结点和右结点。如果不考虑连通性,允许图中有多个连通份量,这样的结构叫做森林。
[编辑]二叉树(Binary Tree)的类型
二叉树是一个有根树(rooted tree),并且每个结点最多有2个子结点。
非空的二叉树, 若树叶总数(leaf/node)为 n0, 分支度为2(Full node)的总数为 n2, 则 n0 = n2 + 1。
满二叉树(Full Binary Tree)是每个结点都有0个或2个子结点的树。
一棵有n个结点的二叉树,按满二叉树的编号方式对它进行编号,若树中所有结点和满二叉树1~n编号完全一致,则称该树为完全二叉树(Complete Binary Tree)。
一棵深度为k且有2k − 1个结点的二叉树称为满二叉树(Full Binary Tree),这种树的特点是每一层上的结点数都是最大结点数。在一棵二叉树中,除最后一层外,若其余层都是满的,并且最后一层或者是满的,或者是在右边缺少连续若干结点,则此二叉树为完全二叉树(Complete Binary Tree)。具有n个结点的完全二叉树的深度为log2n + 1。深度为k的完全二叉树,第k层至少有2k − 1个结点,至多有2k − 1个结点。
Full Binary Tree
Complete Binary Tree
总节点k
k = 2h − 1
2h − 1 < k < 2h − 1
树高h
h = log2(k + 1)
h = log2k + 1
[编辑]存储二叉树的方法
在编程语言中能用多种方法来构造二叉树。在使用记录的编程语言中,二叉树通常用树结点结构来存储。有时也包含指向唯一的父节点的指针。如果一个结点的子结点个数小于2,一些子结点指针可能为空值,或者为特殊的哨兵结点。
二叉树也可以用数组来存储,而且如果这是完全二叉树,这种方法不会浪费空间。用这种紧凑排列,如果一个结点的索引为i,它的子结点能在索引2i+1和2i+2找到,并且它的父节点(如果有)能在索引floor((i-1)/2)找到(假设根节点的索引为0)。这种方法更有利于紧凑存储和更好的访问的局部性,特别是在前序遍历中。然而,它需要连续的存储空间,这样在存储高度为h的n个结点组成的一般普通树时将会浪费很多空间。一种最极坏的情况下如果深度为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)); 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;
[编辑]基本操作
/* 二叉樹的二叉鏈表存儲的基本操作(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) 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;
[编辑]基本操作
/* 二叉樹的三叉鏈表存儲的基本操作(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
和指向两个子结点的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,然后顺序遍历右子树。我们就已经顺序访问了整个树。
- 前序遍历的结果: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
- 先序遍历的序列是:-+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}。它能被转换成右边的二叉树。
- 将一棵树转换为二叉树的方法:
- 在兄弟之间加一连线;
- 对每个结点,除了其左孩子外,去除其与其余孩子之间的联系;
- 以树的根结点为轴心,将整树顺时针转45度。
[编辑]存储结构与基本操作
树的二叉链表表示法(孩子兄弟表示法)是树和二叉树转换的媒介。
[编辑]树的二叉链表存储表示
/* 樹的二叉鏈表(孩子—兄弟)存儲表示 */ typedef struct CSNode { TElemType data; struct CSNode *firstchild,*nextsibling; }CSNode,*CSTree;
[编辑]树的二叉链表存储的基本操作
/* 樹的二叉鏈表(孩子—兄弟)存儲的基本操作(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域指示结点的后继。以这种结点结构构成的二叉链表作为二叉树的存储结构,叫做线索链表,其中指向结点前驱和后继的指针叫做线索,加上线索的二叉树称为线索二叉树。对二叉树以某种次序遍历使其变为线索二叉树的过程叫做线索化。若对二叉树进行中序遍历,则所得的线索二叉树称为中序线索二叉树,线索链表称为为中序线索链表。线索二叉树是一种物理结构。
在中序线索树找结点后继的规律是:若其右标志为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;
[编辑]基本操作
/* 二叉樹的二叉線索存儲的基本操作 */ 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 */ } }