數(shù)據(jù)結(jié)構(gòu)第六章樹課件_第1頁
數(shù)據(jù)結(jié)構(gòu)第六章樹課件_第2頁
數(shù)據(jù)結(jié)構(gòu)第六章樹課件_第3頁
數(shù)據(jù)結(jié)構(gòu)第六章樹課件_第4頁
數(shù)據(jù)結(jié)構(gòu)第六章樹課件_第5頁
已閱讀5頁,還剩175頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)

文檔簡介

1、第六章 樹和二叉樹6.1 樹的類型定義6.2 二叉樹6.3 遍歷和線索二叉樹6.4 樹和森林6.5 樹與等價問題6.6 哈夫曼樹及其應(yīng)用6.1 樹的類型定義數(shù)據(jù)對象 D:D是具有相同特性的數(shù)據(jù)元素的集合。 若D為空集,則稱為空樹; 否則: (1) 在D中存在唯一的稱為根的數(shù)據(jù)元素root, (2) 當(dāng)n1時,其余結(jié)點可分為m (m0)個互 不相交的有限集T1, T2, , Tm, 其中每一 棵子集本身又是一棵符合本定義的樹, 稱為根root的子樹。 數(shù)據(jù)關(guān)系 R:ABCDEFGHIJMKLA( )T1T3T2樹根例如:B(E, F(K, L), C(G), D(H, I, J(M)樹具有下面兩

2、個特點:(1)樹的根結(jié)點沒有前驅(qū)結(jié)點,除根結(jié)點之外的所有結(jié)點有且只有一個前驅(qū)結(jié)點。(2)樹中所有結(jié)點可以有零個或多個后繼結(jié)點。() 有確定的根;() 樹根和子樹根之間為有向關(guān)系。有向樹:有序樹:子樹之間存在確定的次序關(guān)系。無序樹:子樹之間不存在確定的次序關(guān)系?;?本 術(shù) 語結(jié)點(node)結(jié)點的度(degree)分支(branch)結(jié)點葉(leaf)結(jié)點孩子(child)結(jié)點雙親(parent)結(jié)點兄弟(sibling)結(jié)點祖先(ancestor)結(jié)點子孫(descendant)結(jié)點結(jié)點所處層次(level)樹的高度(depth)樹的度(degree)(從根到結(jié)點的)路徑:結(jié)點的層次:樹的深度

3、: 由從根到該結(jié)點所經(jīng)分支和結(jié)點構(gòu)成ABCDEFGHIJMKL假設(shè)根結(jié)點的層次為1,第l 層的結(jié)點的子樹根結(jié)點的層次為l+1樹中葉子結(jié)點所在的最大層次任何一棵非空樹是一個二元組 Tree = (root,F(xiàn))其中:root 被稱為根結(jié)點, F 被稱為子樹森林森林:是 m(m0)棵互不相交的樹的集合ArootBEFKLCGDHIJMF對比樹型結(jié)構(gòu)和線性結(jié)構(gòu)的結(jié)構(gòu)特點線性結(jié)構(gòu)樹型結(jié)構(gòu)第一個數(shù)據(jù)元素 (無前驅(qū)) 根結(jié)點 (無前驅(qū))最后一個數(shù)據(jù)元素 (無后繼)多個葉子結(jié)點 (無后繼)其它數(shù)據(jù)元素(一個前驅(qū)、 一個后繼)其它數(shù)據(jù)元素(一個前驅(qū)、 多個后繼)6.2 二叉樹的類型定義 二叉樹或為空樹;或是由

4、一個根結(jié)點加上兩棵分別稱為左子樹和右子樹的、互不交的二叉樹組成。ABCDEFGHK根結(jié)點左子樹右子樹EF二叉樹的五種基本形態(tài):N空樹只含根結(jié)點NNNLRR右子樹為空樹L左子樹為空樹左右子樹均不為空樹二叉樹的重要特性 性質(zhì) 1 : 在二叉樹的第 i 層上至多有2i-1 個結(jié)點。 (i1)用歸納法證明: 歸納基: 歸納假設(shè): 歸納證明:i = 1 層時,只有一個根結(jié)點, 2i-1 = 20 = 1;假設(shè)對所有的 j,1 j i,命題成立;二叉樹上每個結(jié)點至多有兩棵子樹,則第 i 層的結(jié)點數(shù) = 2i-2 2 = 2i-1 。性質(zhì) 2 : 深度為 k 的二叉樹上至多含 2k-1 個結(jié)點(k1)證明:

5、 基于上一條性質(zhì),深度為 k 的二叉樹上的結(jié)點數(shù)至多為 20+21+ +2k-1 = 2k-1 性質(zhì) 3 : 對任何一棵二叉樹,若它含有n0 個葉子結(jié)點、n2 個度為 2 的結(jié)點,則必存在關(guān)系式:n0 = n2+1證明:設(shè) 二叉樹上結(jié)點總數(shù) n = n0 + n1 + n2又 二叉樹上分支總數(shù) b = n1 + 2n2而 b = n-1 = n0 + n1 + n2 - 1由此, n0 = n2 + 1兩類特殊的二叉樹:滿二叉樹:指的是深度為k且含有2k-1個結(jié)點的二叉樹。完全二叉樹:樹中所含的 n 個結(jié)點和滿二叉樹中編號為 1 至 n 的結(jié)點一一對應(yīng)。12345678910111213141

6、5abcdefghij 性質(zhì) 4 : 具有 n 個結(jié)點的完全二叉樹的深度為 log2n +1證明:設(shè) 完全二叉樹的深度為 k 則根據(jù)第二條性質(zhì)得 2k-1 n 2k 即 k-1 log2 n n,則該結(jié)點無左孩子, 否則,編號為 2i 的結(jié)點為其左孩子結(jié)點;(3) 若 2i+1n,則該結(jié)點無右孩子結(jié)點, 否則,編號為2i+1 的結(jié)點為其右孩子結(jié)點。6.2.4 二叉樹的存儲二、二叉樹的鏈?zhǔn)?存儲表示一、 二叉樹的順序 存儲表示一、 二叉樹的順序存儲表示二叉樹的順序存儲,就是用一組連續(xù)的存儲單元存放二叉樹中的結(jié)點。一般是按照二叉樹結(jié)點從上至下、從左到右的順序存儲。例如: A B D C E F 0

7、 1 2 3 4 5 6 7 8 9 10 11 12 13ABCDEF1401326#define MAXNODE 30/*二叉樹的最大結(jié)點數(shù)*/typedef ELEMTYPE SqBiTreeMAXNODE;/* 0號單元存放根結(jié)點 */SqBiTree bt;即將bt定義為含有MAXNODE個elemtype類型元素的一維數(shù)組。 完全二叉樹和滿二叉樹適合順序存儲二、二叉樹的鏈?zhǔn)酱鎯Ρ硎?. 二叉鏈表2三叉鏈表3雙親鏈表ADEBCFrootlchild data rchild結(jié)點結(jié)構(gòu):1. 二叉鏈表typedef struct BiTNode / 結(jié)點結(jié)構(gòu) ElemType data;

8、struct BiTNode *lchild, *rchild; / 左右孩子指針 BiTNode, *BiTree;lchild data rchild結(jié)點結(jié)構(gòu):C 語言的類型描述如下:rootADEBCF2三叉鏈表parent lchild data rchild結(jié)點結(jié)構(gòu): typedef struct TriTNode / 結(jié)點結(jié)構(gòu) TElemType data; struct TriTNode *lchild, *rchild; / 左右孩子指針 struct TriTNode *parent; /雙親指針 TriTNode, *TriTree;parent lchild data r

9、child結(jié)點結(jié)構(gòu):C 語言的類型描述如下:結(jié)點結(jié)構(gòu):3雙親鏈表 data parentABDCEF0B41D42C03E14A-15F36LRTagLRRRL根 typedef struct BPTNode / 結(jié)點結(jié)構(gòu) TElemType data; int *parent; / 指向雙親的指針 char LRTag; / 左、右孩子標(biāo)志域 BPTNode typedef struct BPTree / 樹結(jié)構(gòu) BPTNode nodesMAX_TREE_SIZE; int num_node; / 結(jié)點數(shù)目 int root; / 根結(jié)點的位置 BPTree6.3.1二叉樹的遍歷一、問題的

10、提出二、先左后右的遍歷算法三、算法的遞歸描述四、中序遍歷算法的非遞歸描述五、遍歷算法的應(yīng)用舉例 順著某一條搜索路徑巡訪二叉樹中的結(jié)點,使得每個結(jié)點均被訪問一次,而且僅被訪問一次。一、問題的提出“訪問”的含義可以很廣,如:輸出結(jié)點的信息等。 “遍歷”是任何類型均有的操作,對線性結(jié)構(gòu)而言,只有一條搜索路徑(因為每個結(jié)點均只有一個后繼),故不需要另加討論。而二叉樹是非線性結(jié)構(gòu), 每個結(jié)點有兩個后繼,則存在如何遍歷即按什么樣的搜索路徑進行遍歷的問題。 對“二叉樹”而言,可以有三條搜索路徑:1先上后下的按層次遍歷;2先左(子樹)后右(子樹)的遍歷;3先右(子樹)后左(子樹)的遍歷。abcdefghij二

11、、先左后右的遍歷算法先(根)序的遍歷算法中(根)序的遍歷算法后(根)序的遍歷算法根左子樹右子樹根根根根根 若二叉樹為空樹,則空操作;否則,(1)訪問根結(jié)點;(2)先序遍歷左子樹;(3)先序遍歷右子樹。先(根)序的遍歷算法:ADBCD L RAD L RD L RBDCD L R先序遍歷序列:A B D C先序遍歷: 若二叉樹為空樹,則空操作;否則,(1)中序遍歷左子樹;(2)訪問根結(jié)點;(3)中序遍歷右子樹。中(根)序的遍歷算法:ADBCL D RBL D RL D RADCL D R中序遍歷序列:B D A C中序遍歷: 若二叉樹為空樹,則空操作;否則,(1)后序遍歷左子樹;(2)后序遍歷右

12、子樹;(3)訪問根結(jié)點。后(根)序的遍歷算法:ADBC L R DL R DL R DADCL R D后序遍歷序列: D B C A后序遍歷:B-+/a*b-efcd先序遍歷:中序遍歷:后序遍歷:層次遍歷:-+a*b-cd/ef-+a*b-cd/ef-+a*b-cd/ef-+a*b-cd/efABCDEFGHK例如:先序序列:中序序列:后序序列:A B C D E F G H KB D C A E H G K FD C B H K G F E A三、算法的遞歸描述void Preorder (BiTree T, void( *visit)(TElemType& e) / 先序遍歷二叉樹 if

13、(T) visit(T-data); / 訪問結(jié)點 Preorder(T-lchild, visit); / 遍歷左子樹 Preorder(T-rchild, visit);/ 遍歷右子樹 void preorder(JD *bt) if(bt!=NULL) printf(%dt,bt-data); preorder(bt-lchild); preorder(bt-rchild); 主程序Pre( T )返回返回pre(T R);返回返回pre(T R);ACBDTBprintf(B);pre(T L);BTAprintf(A);pre(T L);ATDprintf(D);pre(T L);D

14、TCprintf(C);pre(T L);C返回T左是空返回pre(T R);T左是空返回T右是空返回T左是空返回T右是空返回pre(T R);先序序列:A B D C中序遍歷void inorder(JD *bt) if(bt!=NULL) inorder(bt-lchild); printf(%dt,bt-data); inorder(bt-rchild); 后序遍歷void postorder(JD *bt) if(bt!=NULL) postorder(bt-lchild); postorder(bt-rchild); printf(%dt,bt-data); 中序遍歷算法的非遞歸描述

15、有兩種分析(描述)方法:一、“任務(wù)書”分析方法二、“路徑”分析方法在寫算法之前首先需定義棧的元素類型。typedef enum Travel, Visit TaskType; / Travel = 1:遍歷, / Visit = 0:訪問 typedef struct BiTree ptr; / 指向根結(jié)點的指針 TaskType task; / 任務(wù)性質(zhì) ElemType;“遍歷二叉樹”包括三項子任務(wù):“遍歷左子樹”“遍歷右子樹”“訪問根結(jié)點”void InOrder_iter( BiTree BT ) / 利用棧實現(xiàn)中序遍歷二叉樹,T為指向二叉樹的根結(jié)點的頭指針 InitStack(S);

16、 e.ptr=BT; e.task=Travel; if(T) Push(S, e); / 布置初始任務(wù) while(!StackEmpty(S) Pop(S,e); if (e.task=Visit) visit(e.ptr); else . . . /while/InOrder_iterif(!e.ptr) / 處理非空樹的遍歷任務(wù) p=e.ptr; e.ptr=p-rchild; Push(S,e); / 最不迫切任務(wù)進棧 e.ptr=p; e.task=Visit; Push(S,e); e.ptr=p-lchild; e.task=Travel; Push(S,e); /if voi

17、d Inorder_I(BiTree T, void (*visit) (TelemType& e) Stack *S; t = GoFarLeft(T, S); / 找到最左下的結(jié)點 while(t) visit(t-data); if (t-rchild) else if ( !StackEmpty(S ) t = Pop(S); / 退棧 else t = NULL; / ??毡砻鞅闅v結(jié)束 / while/ Inorder_I t = GoFarLeft(t-rchild, S);BiTNode *GoFarLeft(BiTree T, Stack *S) if (!T ) return

18、 NULL; while (T-lchild ) Push(S, T); T = T-lchild; return T;中序的非遞歸算法:void inorder(JD *bt) int i=0; JD *p,*sM; p=bt; do while(p!=NULL) si+=p; p=p-lchild; if(i0) p=s-i; printf(%dt,p-data); p=p-rchild; while(i0|p!=NULL);非遞歸算法ABCDEFGpiP-A(1)ABCDEFGpiP-AP-B(2)ABCDEFGpiP-AP-BP-C(3)p=NULLABCDEFGiP-AP-B訪問:C

19、(4)pABCDEFGiP-A訪問:C B(5)ABCDEFGiP-AP-D訪問:C Bp(6)ABCDEFGiP-AP-DP-E訪問:C Bp(7)ABCDEFGiP-AP-D訪問:C B Ep(8)ABCDEFGiP-AP-DP-G訪問:C B EP=NULL(9)ABCDEFGiP-A訪問:C B E G Dp(11)ABCDEFGiP-AP-F訪問:C B E G Dp(12)ABCDEFGiP-AP-D訪問:C B E Gp(10)ABCDEFGiP-A訪問:C B E G D Fp=NULL(13)ABCDEFGi訪問:C B E G D F Ap(14)ABCDEFGi訪問:C

20、B E G D F Ap=NULL(15)層次遍歷二叉樹的層次遍歷,是指從二叉樹的第一層(根結(jié)點)開始,從上至下逐層遍歷,在同一層中,則按從左到右的順序?qū)Y(jié)點逐個訪問。按層次遍歷所得到的結(jié)果序列為:A B C D E F Gabcdefg在進行層次遍歷時,可設(shè)置一個隊列結(jié)構(gòu),遍歷從二叉樹的根結(jié)點開始,首先將根結(jié)點指針入隊列,然后從隊頭取出一個元素,每取一個元素,執(zhí)行下面兩個操作:(1)訪問該元素所指結(jié)點;(2)若該元素所指結(jié)點的左、右孩子結(jié)點非空,則將該元素所指結(jié)點的左孩子指針和右孩子指針順序入隊。void LevelOrder(BiTree bt/ 層次遍歷二叉樹btBiTree queue

21、MAXNODE; int front,rear;if (bt=NULL) return;Front = -1; rear=0;queuerear=bt;while(front!=rear) front+; Visite(queuefront-data); /*訪問隊首結(jié)點的數(shù)據(jù)域*/ if (queuefront-rchild!=NULL) /* 將隊首結(jié)點的右孩結(jié)點入隊 */ rear+; queuerear = queuefront-rchild; if (queuefront-lchild!=NULL) /* 將隊首結(jié)點的左孩結(jié)點入隊 */ rear+; queuerear = queu

22、efront-lchild; 四、遍歷算法的應(yīng)用舉例2、統(tǒng)計二叉樹中葉子結(jié)點的個數(shù)3、求二叉樹的深度(后序遍歷)4、復(fù)制二叉樹(后序遍歷)5、建立二叉樹的存儲結(jié)構(gòu)1、查詢二叉樹中某個結(jié)點1. 在二叉樹不空的前提下,和根結(jié)點的元素進行比較,若相等,則找到返回 TRUE;2. 否則在左子樹中進行查找,若找到,則返回 TRUE;3. 否則繼續(xù)在右子樹中進行查找,若找到,則返回 TRUE,否則返回 FALSE;Status Preorder (BiTree T, ElemType x, BiTree &p) / 若二叉樹中存在和 x 相同的元素,則 p 指向該結(jié)點并返回 OK,/ 否則返回 FALSE

23、 if (T) if (T-data=x) p = T; return OK, /if else return FALSE;else if (Preorder(T-lchild, x, p) return OK; else return(Preorder(T-rchild, x, p) ;/else2、統(tǒng)計二叉樹中葉子結(jié)點的個數(shù)算法基本思想: 先序(或中序或后序)遍歷二叉樹,在遍歷過程中查找葉子結(jié)點,并計數(shù)。由此,需在遍歷算法中增添一個“計數(shù)”的參數(shù),并將算法中“訪問結(jié)點” 的操作改為:若是葉子,則計數(shù)器增1。void CountLeaf (BiTree T, int& count) if (

24、 T ) if (!T-lchild)& (!T-rchild) count+; / 對葉子結(jié)點計數(shù) CountLeaf( T-lchild, count); CountLeaf( T-rchild, count); / if / CountLeafint CountLeaf (BiTree T)/返回指針T所指二叉樹中所有葉子結(jié)點個數(shù) if (!T ) return 0; if (!T-lchild & !T-rchild) return 1; else m = CountLeaf( T-lchild); n = CountLeaf( T-rchild); return (m+n); /el

25、se / CountLeafint Count (BiTree T)/返回指針T所指二叉樹中所有結(jié)點個數(shù) if (!T ) return 0; if (!T-lchild & !T-rchild) return 1; else m = Count ( T-lchild); n = Count ( T-rchild); return (m+n+1); /else / CountLeaf3、求二叉樹的深度(后序遍歷)算法基本思想: 從二叉樹深度的定義可知,二叉樹的深度應(yīng)為其左、右子樹深度的最大值加1。由此,需先分別求得左、右子樹的深度,算法中“訪問結(jié)點”的操作為:求得左、右子樹深度的最大值,然后加

26、 1 。 首先分析二叉樹的深度和它的左、右子樹深度之間的關(guān)系。int Depth (BiTree T ) / 返回二叉樹的深度 if ( !T ) depthval = 0; else depthLeft = Depth( T-lchild ); depthRight= Depth( T-rchild ); depthval = 1 + (depthLeft depthRight ? depthLeft : depthRight); return depthval;void Depth(BiTree T , int level, int &dval) if ( T ) if (leveldva

27、l) dval = level; Depth( T-lchild, level+1, dval ); Depth( T-rchild, level+1, dval ); / 調(diào)用之前 level 的初值為 1。 / dval 的初值為 0.4、復(fù)制二叉樹其基本操作為:生成一個結(jié)點。根元素T左子樹右子樹根元素NEWT左子樹右子樹左子樹右子樹(后序遍歷)BiTNode *GetTreeNode(TElemType item, BiTNode *lptr , BiTNode *rptr ) if (!(T = new BiTNode) exit(1); T- data = item; T- lchi

28、ld = lptr; T- rchild = rptr; return T; 生成一個二叉樹的結(jié)點(其數(shù)據(jù)域為item,左指針域為lptr,右指針域為rptr)BiTNode *CopyTree(BiTNode *T) if (!T ) return NULL; if (T-lchild ) newlptr = CopyTree(T-lchild); /復(fù)制左子樹 else newlptr = NULL; if (T-rchild ) newrptr = CopyTree(T-rchild); /復(fù)制右子樹 else newrptr = NULL; newT = GetTreeNode(T-d

29、ata, newlptr, newrptr); return newT; / CopyTreeABCDEFGHK D C H K G A例如:下列二叉樹的復(fù)制過程如下:newT F B E 5、建立二叉樹的存儲結(jié)構(gòu)不同的定義方法相應(yīng)有不同的存儲結(jié)構(gòu)的建立算法 以字符串的形式 “根 左子樹 右子樹”定義一棵二叉樹例如:以空白字符“ ”表示ABCDA(B( ,C( , ),D( , )空樹只含一個根結(jié)點的二叉樹A以字符串“A ”表示以下列字符串表示Status CreateBiTree(BiTree &T) scanf(&ch); if (ch= ) T = NULL; else if (!(T

30、= new BiTNode) exit(OVERFLOW); T-data = ch; / 生成根結(jié)點 CreateBiTree(T-lchild); / 構(gòu)造左子樹 CreateBiTree(T-rchild); / 構(gòu)造右子樹 return OK; / CreateBiTreeA B C D ABCD上頁算法執(zhí)行過程舉例如下:ATBCDscanf(&ch);if (ch= ) T = NULL;else if (!(T = new BiTNode) exit(OVERFLOW); T-data = ch;CreateBiTree(T-lchild); CreateBiTree(T-rchi

31、ld); 按給定的表達式建相應(yīng)二叉樹 由先綴表示式建樹例如:已知表達式的先綴表示式 -+ a b c / d e 由原表達式建樹例如:已知表達式 (a+b)c d/e對應(yīng)先綴表達式 -+ a b c / d e的二叉樹abcde-+/特點: 操作數(shù)為葉子結(jié)點, 運算符為分支結(jié)點scanf(&ch);if ( In(ch, 字母集 ) 建葉子結(jié)點;else 建根結(jié)點; 遞歸建左子樹; 遞歸建右子樹;由先綴表示式建樹的算法的基本操作:a+b(a+b)c d/ea+bc 分析表達式和二叉樹的關(guān)系:abbac+abc(a+b)c/deabc+-基本操作:scanf(&ch);if (In(ch, 字母

32、集 ) 建葉子結(jié)點; 暫存; else if (In(ch, 運算符集) 和前一個運算符比較優(yōu)先數(shù); 若當(dāng)前的優(yōu)先數(shù)“高”,則暫存; 否則建子樹;void CrtExptree(BiTree &T, char exp ) InitStack(S); Push(S, #); InitStack(PTR); p = exp; ch = *p; while (!(GetTop(S)=# & ch=#) if (!IN(ch, OP) CrtNode( t, ch ); / 建葉子結(jié)點并入棧 else if ( ch!= # ) p+; ch = *p; / while Pop(PTR, T); /

33、CrtExptree switch (ch) case ( : Push(S, ch); break; case ) : Pop(S, c); while (c!= ( ) CrtSubtree( t, c); / 建二叉樹并入棧 Pop(S, c) break; defult : / switch while(!Gettop(S, c) & ( precede(c,ch) CrtSubtree( t, c); Pop(S, c);if ( ch!= # ) Push( S, ch); break;建葉子結(jié)點的算法為:void CrtNode(BiTree& T,char ch) if (!(

34、T= new BiTNode) exit(OVERFLOW); T-data = char; T-lchild = T-rchild = NULL; Push( PTR, T );建子樹的算法為:void CrtSubtree (Bitree& T, char c) if (!(T= new BiTNode) exit(OVERFLOW); T-data = c; Pop(PTR, rc); T-rchild = rc; Pop(PTR, lc); T-lchild = lc; Push(PTR, T); 僅知二叉樹的先序序列“abcdefg” 不能唯一確定一棵二叉樹,由二叉樹的先序和中序序列

35、建樹 如果同時已知二叉樹的中序序列“cbdaegf”,則會如何? 二叉樹的先序序列二叉樹的中序序列左子樹左子樹右子樹右子樹根根a b c d e f gc b d a e g f例如:aab bccddeeffggabcdefg先序序列中序序列void CrtBT(BiTree& T, char pre, char ino, int ps, int is, int n ) / 已知preps.ps+n-1為二叉樹的先序序列, / inois.is+n-1為二叉樹的中序序列,本算 / 法由此兩個序列構(gòu)造二叉鏈表 if (n=0) T=NULL; else k=Search(ino, preps)

36、; / 在中序序列中查詢 if (k= -1) T=NULL; else / / CrtBT if (!(T= new BiTNode) exit(OVERFLOW);T-data = preps;if (k=is) T-Lchild = NULL;else CrtBT(T-Lchild, pre, ino, ps+1, is, k-is );if (k=is+n-1) T-Rchild = NULL;else CrtBT(T-Rchild, pre, ino, ps+1+(k-is), k+1, n-(k-is)-1 ); 6.4 樹和森林 的表示方法樹的三種存儲結(jié)構(gòu)一、雙親表示法二、孩子鏈

37、表表示法三、樹的二叉鏈表(孩子-兄弟) 存儲表示法ABCDEFGr=0n=60 A -11 B 02 C 03 D 04 E 2 5 F 26 G 5data parent一、雙親表示法: typedef struct PTNode Elem data; int parent; / 雙親位置域 PTNode; data parent#define MAX_TREE_SIZE 100結(jié)點結(jié)構(gòu):C語言的類型描述:typedef struct PTNode nodesMAX_TREE_SIZE; int r, n; / 根結(jié)點的位置和結(jié)點個數(shù) PTree;樹結(jié)構(gòu):二、孩子鏈表表示法:多重鏈表:每個結(jié)

38、點有多個指針域,分別指向其子樹的根結(jié)點同構(gòu):結(jié)點的指針個數(shù)相等,為樹的度D結(jié)點不同構(gòu):結(jié)點指針個數(shù)不等,為該結(jié)點的度ddata child1 child2 . childDdata degree child1 child2 . childdr=0n=6 dataABCDEFG0 A -11 B 02 C 03 D 04 E 25 F 26 G 464 5 1 2 3二、孩子鏈表表示法:-1 0 0 0 2 2 4firstchildparenttypedef struct CTNode int child; struct CTNode *nextchild; *ChildPtr;孩子結(jié)點結(jié)構(gòu):

39、 child nextchildC語言的類型描述: typedef struct Elem data; ChildPtr firstchild; / 孩子鏈的頭指針 CTBox;雙親結(jié)點結(jié)構(gòu) data firstchildtypedef struct CTBox nodesMAX_TREE_SIZE; int n, r; / 結(jié)點數(shù)和根結(jié)點的位置 CTree;樹結(jié)構(gòu):ABCDEFGroot AB C E D F G AB C E D F G 三、樹的二叉鏈表 (孩子-兄弟)存儲表示法roottypedef struct CSNode Elem data; struct CSNode *firs

40、tchild, *nextsibling; CSNode, *CSTree;C語言的類型描述:結(jié)點結(jié)構(gòu): firstchild data nextsibling將一棵樹轉(zhuǎn)換為二叉樹的方法是:(1)樹中所有相鄰兄弟之間加一條連線。(2)對樹中的每個結(jié)點,只保留它與第一個孩子結(jié)點之間的連線,刪去它與其它孩子結(jié)點之間的連線。(3)以樹的根結(jié)點為軸心,將整棵樹順時針轉(zhuǎn)動一定的角度,使之結(jié)構(gòu)層次分明。63 二叉樹與樹、森林之間的轉(zhuǎn)換631 樹轉(zhuǎn)換為二叉樹632 森林轉(zhuǎn)換為二叉樹 森林轉(zhuǎn)換為二叉樹的方法如下:(1)將森林中的每棵樹轉(zhuǎn)換成相應(yīng)的二叉樹(2)第一棵二叉樹不動,從第二棵二叉樹開始,依次把后一棵二

41、叉樹的根結(jié)點作為前一棵二叉樹根結(jié)點的右孩子,當(dāng)所有二叉樹連起來后,此時所得到的二叉樹就是由森林轉(zhuǎn)換得到的二叉樹。633 二叉樹轉(zhuǎn)換為樹和森林(1)若某結(jié)點是其雙親的左孩子,則把該結(jié)點的右孩子、右孩子的右孩子,都與該結(jié)點的雙親結(jié)點用線連起來;(2)刪去原二叉樹中所有的雙親結(jié)點與右孩子結(jié)點的連線; 由此,樹和森林的各種操作均可與二叉樹的各種操作相對應(yīng)。 應(yīng)當(dāng)注意的是,和樹對應(yīng)的二叉樹,其左、右子樹的概念已改變?yōu)椋?左是孩子,右是兄弟6.4 哈 夫 曼 樹 與 哈 夫 曼 編 碼 最優(yōu)樹的定義 如何構(gòu)造最優(yōu)樹 前綴編碼 一、最優(yōu)樹的定義樹的路徑長度定義為: 樹中每個結(jié)點的路徑長度之和。 結(jié)點的路徑長

42、度定義為: 從根結(jié)點到該結(jié)點的路徑上 分支的數(shù)目。 樹的帶權(quán)路徑長度定義為: 樹中所有葉子結(jié)點的帶權(quán)路徑長度之和 WPL(T) = wklk (對所有葉子結(jié)點) 在所有含 n 個葉子結(jié)點、并帶相同權(quán)值的 m 叉樹中,必存在一棵其帶權(quán)路徑長度取最小值的樹,稱為“最優(yōu)樹”。例如:27 9 75492WPL(T)= 72+52+23+43+92 =60WPL(T)= 74+94+53+42+21 =89 54根據(jù)給定的 n 個權(quán)值 w1, w2, , wn構(gòu)造 n 棵二叉樹的集合 F = T1, T2, , Tn,其中每棵二叉樹中均只含一個帶權(quán)值 為 wi 的根結(jié)點,其左、右子樹為空樹;二、如何構(gòu)造

43、最優(yōu)樹(1)(赫夫曼算法) 以二叉樹為例: 在 F 中選取其根結(jié)點的權(quán)值為最 小的兩棵二叉樹,分別作為左、 右子樹構(gòu)造一棵新的二叉樹,并 置這棵新的二叉樹根結(jié)點的權(quán)值 為其左、右子樹根結(jié)點的權(quán)值之 和;(2) 從F中刪去這兩棵樹,同時加入 剛生成的新樹; 重復(fù) (2) 和 (3) 兩步,直至 F 中只 含一棵樹為止。(3)(4)9例如: 已知權(quán)值 W= 5, 6, 2, 9, 7 562752769767139527671395279527166713292.哈夫曼樹的造構(gòu)造算法根據(jù)二叉樹的性質(zhì)可知,具有n個葉子結(jié)點的哈夫曼樹共有2n1個結(jié)點,所以數(shù)組HuffNode的大小設(shè)置為2n1weig

44、htlchildrchildparent下面給出哈夫曼樹的構(gòu)造算法。#define MAXVALUE 10000 /* 定義最大權(quán)值 */#define MAXLEAF 30 /*定義哈夫曼樹中葉子結(jié)點個數(shù)*/#define MAXNODE MAXLEAF*2-1typedef structint weight; int parent; int lchild,rchild;HNodeType;for (i=0;i2*n-1;i+) /* 數(shù)組HuffNode 初始化 */ HuffNodei.weight=0; HuffNodei.parent=-1; HuffNodei.lchild=-1;

45、 HuffNodei.rchild=-1; for (i=0;in;i+) scanf(“%d”,&HuffNodei.weight);/* 將找出的兩棵子樹合并為一棵子樹 */ HuffNodex1.parent=n+i; HuffNodex2.parent=n+i; HuffNoden+i.weight= HuffNodex1.weight+HuffNodex2.weight; HuffNoden+i.lchild=x1; HuffNoden+i.rchild=x2; for (i=0;in-1;i+) m1=m2=MAXVALUE; x1=x2=0; for (j=0;jn+i;j+)

46、if (HuffNodej.weightm1 & HuffNodej.parent=-1) m2=m1; x2=x1; m1=HuffNodej.weight; x1=j; else if (HuffNodej.weightfirstchild);D2 = Depth(T-nextsibling);Return Maxd1+1,d2int TreeDepth( CTree T ) / T 是樹的孩子鏈表存儲結(jié)構(gòu), / 返回該樹的深度 if ( T.n = 0) return 0; else return Depth( T, T.r ); / TreeDepth一、求樹的深度的算法:int De

47、pth( CTree T, int root ) max = 0; p = T.nodesroot.firstchild; while ( p ) h = Depth( T, p-child ); if ( h max ) max = h; p = p-nextchild; /while return max+1;二、輸出樹中所有從根到葉子的路徑的算法: A B C DE F G H I J K例如:對左圖所示的樹,其輸出結(jié)果應(yīng)為:A B EA B FA CA D G H IA D G H JA D G H Kvoid AllPath( BiTree T, Stack& S ) if (T)

48、Push( S, T-data ); if (!T-Lchild & !T-Rchild ) PrintStack(S); else AllPath( T-Lchild, S ); AllPath( T-Rchild, S ); Pop(S); / if(T) / AllPath/ 輸出二叉樹上從根到所有葉子結(jié)點的路徑void OutPath( Bitree T, Stack& S ) while ( !T ) Push(S, T-data ); if ( !T-firstchild ) Printstack(S); else OutPath( T-firstchild, S ); Pop(S

49、); T = T-nextsibling; / while / OutPath/ 輸出森林中所有從根到葉的路徑三、建樹的存儲結(jié)構(gòu)的算法: 和二叉樹類似,不同的定義相應(yīng)有不同的算法。 假設(shè)以二元組(F,C)的形式自上而下、自左而右依次輸入樹的各邊,建立樹的孩子-兄弟鏈表。ABCDEFG例如:對下列所示樹的輸入序列應(yīng)為:(#, A)(A, B)(A, C)(A, D)(C, E)(C, F)(E, G)ABCD(#, A)(A, B)(A, C)(A, D)(C, E)可見,算法中需要一個隊列保存已建好的結(jié)點的指針void CreatTree( CSTree &T ) T = NULL; for(

50、 scanf(&fa, &ch); ch!= ; scanf(&fa, &ch);) p = GetTreeNode(ch); / 創(chuàng)建結(jié)點EnQueue(Q, p); / 指針入隊列if (fa = ) T = p; / 所建為根結(jié)點 else / 非根結(jié)點的情況 / for / CreateTree GetHead(Q,s); / 取隊列頭元素(指針值)while (s-data != fa ) / 查詢雙親結(jié)點 DeQueue(Q,s); GetHead(Q,s); if (!(s-firstchild) s-firstchild = p; r = p; / 鏈接第一個孩子結(jié)點else

51、r-nextsibling = p; r = p; / 鏈接其它孩子結(jié)點 本章作業(yè) 6.33, 6.34, 6.43, 6.45 6.47, 6.51, 6.56 6.59, 6.61, 6.63, 6.686.5線索二叉樹 何謂線索二叉樹? 線索鏈表的遍歷算法 如何建立線索鏈表?一、何謂線索二叉樹?遍歷二叉樹的結(jié)果是, 求得結(jié)點的一個線性序列。ABCDEFGHK例如:先序序列: A B C D E F G H K中序序列: B D C A H G K F E后序序列: D C B H K G F E A指向該線性序列中的“前驅(qū)”和 “后繼” 的指針,稱作“線索”與其相應(yīng)的二叉樹,稱作 “線索二叉樹”包含 “線索” 的存儲結(jié)構(gòu),稱作 “線索鏈表”A B C D E F G H K D C B E 對線索鏈表中結(jié)點的約定: 在二叉鏈表的結(jié)點中增加兩個標(biāo)志域,并作如下規(guī)定:若該結(jié)點的左子樹不空,則Lchild域的指針指向其左子樹, 且左標(biāo)志域的值為“指針 Link”; 否則,Lchild域的指針指向其“前驅(qū)”, 且左標(biāo)志的值為“線索 Thread” 。若該結(jié)點的右子樹不空,則rchild域的指針指向其右子樹, 且右標(biāo)志域的值為 “指針 Link”;否則,rchild域的指針指向其“后繼”, 且右標(biāo)志的

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論