一.樹的定義
- 有且僅有一個特點的稱為根的節點
- 當n>1時,其余節點可分為m(m>0)個互不相干的有限交集,每個交集稱為根的子樹。
二.森林的定義
- m個互不相交的森林樹的集合,子樹的集合稱為子樹的森林。
三.樹的存儲結構
1.雙親表示法
- 在樹中,除了根節點沒有雙親外,其他節點的雙親的唯一確定的。
//雙親存儲的結構類型定義 typedef struct PTNode{ TElemType data; //數據域 int parent; //雙親的位置,根節點的雙親為-1 }PTNode; //雙親的節點類型 typedef struct { PTNode *nodes; //初始化分配的結點數組 int r,nodeNum; //根的位置和結點數 }PTree; //樹的雙親存儲結構類型
優點: 可用parent直接找到雙親,並且很容易找到祖先。
缺點:需要查找節點的孩子及其子孫時要遍歷整個結構。
2.雙親孩子表示法
- 是對雙親表示法的擴展,為各節點構造孩子單鏈表,以便於訪問結點的孩子及其子孫,在結點的數組元素中增加firstchild域作為結點的孩子鏈表頭的頭指針。
//雙親孩子存儲結構的類型定義 typedef struct ChildNode{ int childIndex; //孩子在結點數組的位置 struct ChildNode *nextChild; //下一個孩子 }ChildNode; //孩子鏈表中的節點類型 typdef struct{ TElemType data; //元素值 int parent; //雙親的位置 struct ChildNode *firstChild; //孩子鏈表頭指針 }PCTreeNode; //雙親節點的節點類型 typedef struct{ PCTreeNode *nodes; //節點數組 int nodeNum,r; //結點元素的個數,根位置 }PCTree; //樹的雙親孩子存儲結構類型
3.孩子兄弟表示法
- 在樹中,結點的最左孩子(第一個孩子)和右兄弟如果存在則都是唯一的。采取二叉鏈式存儲結構,每個結點包含三個域,元素值data,最左邊孩子指針firstchild和右兄弟指針nextsibling。
//孩子兄弟鏈表的類型定義 typedef struct CSTNode{ TElemType data; //數據域 struct CSTNode *firstChild,*nextSibling; //最左孩子指針,右兄弟指針 }CSTnode,*CSTree,*CSForest; //孩子兄弟鏈表
4.孩子兄弟表示法的樹的接口
Status InitTree(CSTree &T); //構造空樹 CSTree MakeTree(TElemType e,int n....) //創建根結點為e和n顆子樹的樹 Status DestroyTree(CSTree &T) //銷毀樹 int TreeDepth(CSTree T) //返回樹的深度 CSNode *Search(CSTree T,TElemType e); //查找樹T中的節點e並返回其指針 Status InesertChild(CSTree &T,int i,CSTree c) //插入c為T的第i顆子樹,c非空並且與T不相交 Status DeleteChild(CSTree &T,int i) //刪除第i棵子樹
1.創建樹
#include<stdarg.h>// 標准頭文件,提供宏va_start、va_arg和va_end, CSTree MakeTree(TElemType e,int n....){ int i; CSTree t,p,pi; va_list argptr; //存放變長參數表信息的數組 t=(CSTree)malloc(sizeof(CSTNode)); if(t=NULL) return NULL; t->data=e; //根結點的值為e; t->firstChild=t->nextSibling=NULL; if(n<=0) return t; //若無子樹,則返回根結點 va_start(argptr,n) //令argptr 指向n后的第一個實參 p=va_arg(argptr,CSTree); //取第一棵子樹的實參轉化為CSTree類型 t->firstChild=p; pi=p; for(i=1;i<n;i++){ p=va_arg(argptr,CSTree); //取下一顆子樹的實參並轉換為CSTree類型 pi->nextSibling=p; pi=p; } va_end(argptr); return t; }
2.插入第i個子樹
Status InesertChild(CSTree &T,int i,CSTree c){ int j; CSTree p; if(NULL==T||i<1) return ERROR; if(i==1){ //c為第一棵插入子樹 c->nextSibling=T->firstChild; T->firstChild=c; //T成為T的第一棵子樹 }else{ p=T->firstChild; for(j=2;p!=NULL&&j<i;j++){ p=p->nextSibling; //尋找插入位置 } if(j==i){ c->nextSibling = p->nextSibling; p->nextSibling = c; }else return ERROR; } return OK; }
3.求樹的深度
int TreeDepth(CSTree T) { // 求樹T的深度 int dep1, dep2, dep; if(NULL==T) dep = 0; // 樹為空,深度則為0 else { dep1 = TreeDepth(T->firstChild); // 求T的子樹森林的深度 dep2 = TreeDepth(T->nextSibling); // 求除T所在樹以外的其余樹的深度 dep = dep1+1>dep2 ? dep1+1 : dep2; // 樹的深度 } return dep; }
4.查找樹
CSTreeNode* Search(CSTree T, TElemType e) { // 查找樹T中的結點e並返回其指針 CSTreeNode* result = NULL; if(NULL==T) return NULL; // 樹為空,返回NULL if(T->data==e) return T; // 找到結點,返回其指針 if((result = Search(T->firstChild, e))!=NULL) // 在T的子樹森林查找 return result; return Search(T->nextSibling, e); // 在除T所在樹以外的其余樹構成的森林查找 }
1.三種遍歷
1.先序(次序)遍歷
- 若樹不空,則先訪問根結點,然后依次先序遍歷各棵子樹。
2.后序(次序)遍歷
- 若樹不空,則先依次后序遍歷各棵子樹,然后訪問根結點。
3.層序遍歷
- 若樹不空,則自上而下自左至右訪問樹中每個結點。
2.樹的先序遍歷
若樹不為空,則依次進行以下操作:
(1)訪問根結點;
(2)訪問根結點T所在的子樹森林;
(3)除根結點所在樹以外的其余樹構成的森林。
Status PreOrderTraverseTree(CSTree T, Status(*visit)(TElemType)) { if(NULL==T) return OK;//空樹 if(ERROR==visit(T->data)) return ERROR; //訪問根節點 if(ERROR==PreOrderTraverseTree(T->firstChild,visit)) //訪問左樹 return ERROR; return PreOrderTraverseTree(T->nextSibling,visit); // 遞歸訪問除T所在樹以外的其余樹構成的森林 }