资源预览内容
第1页 / 共50页
第2页 / 共50页
第3页 / 共50页
第4页 / 共50页
第5页 / 共50页
第6页 / 共50页
第7页 / 共50页
第8页 / 共50页
第9页 / 共50页
第10页 / 共50页
亲,该文档总共50页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
第6章 树本章要点树的相关概念、树的表示、逻辑特征二叉树的相关概念、二叉树的性质二叉树的存储和遍历树、森林和二叉树的转换二叉树的应用(哈夫曼树的构造和编码) 通过本章学习,应深入掌握树的相关概念、树的表示和树的性质;二叉树的相关概念、二叉树的性质、二叉树的逻辑结构和存储结构,二叉树的基本运算以及实现算法,二叉树的应用。 树是一类重要的非线性数据结构,是以分支关系定义的层次结构。6.1 树的定义v 定义定义:树(tree)是n(n0)个结点的有限集T,它满足如下两个条件:(1)有且仅有一个特定的称为根(root)的结点。(2)当n1时,其余的结点可分为m(m 0)个互不相交的有限集合T1,T2,Tm,其中每一个集合本身又是一棵树,称为根的子树(subtree)特点:o 树中至少有一个结点根o 树中各子树是互不相交的集合A只有根结点的树子树ABCDEFGHIJKLM有子树的树根图只有根结点和有子树的树* 一棵树是由若干棵子树构成,而子树又可由若干棵更小的子树构成。v树的基本概念结点的度(degree)一个结点的子树个数叶子(leaf)度为0的结点(或称为终端结点或非终端结点)孩子(child)树中某个结点的子树之根称为该结点的孩子。双亲(parents)孩子结点的上层结点叫该结点的兄弟(sibling)同一双亲的孩子子孙结点(Descendant)一个结点的所有子树中的结点称为该结点的子孙结点。祖先结点(Ancestor)从树根结点到达一个结点的路径上通过的所有结点称为该结点的祖先结点。树的度一棵树中结点最大的度数结点的层数(level)从根结点算起,根为第一层,它的孩子为第二层树的深度(depth)或高度(Height)树中结点的最大层数森林(forest)m(m 0)棵互不相交的树的集合路径(path)或道路有序树(ordered tree):树中结点的各子树从左至右是有次序的(不能互换)。无序树(unordered tree):树形结构的逻辑特征: 树中任一结点都可以有零个或多个后继(即孩子)结点,但至多只能有一个前趋(即双亲)结点。树中只有根结点无前趋,叶子结点无后继。显然,父子关系是非线性的,故树形结构是非线性结构。祖先和子孙的关系是对父子关系的延拓,它定义了树中结点的纵向次序。ABCDEFGHIJKLM结点A的度:3结点B的度:2结点M的度:0结点A的孩子:B,C,D结点B的孩子:E,F结点A的层次:1结点M的层次:4树的度:3叶子:K,L,F,G,M,I,J结点I的双亲:D结点L的双亲:E结点B,C,D为兄弟结点K,L为兄弟树的深度:4结点F,G为堂兄弟结点A是结点F,G的祖先 图6.2 树v树的表示 树有树形表示法、嵌套集合表示法、凹入表表示法、广义表表示法等。一个如下逻辑结构的树: T=(K,N) K=a,b,c,d,e,f,g,h,i,j N=,a(b(d,e(i,j),f),c(g,h)(d) 嵌套括号表示法图6.3 树的各种表示法6.2 二叉树定义v定义:二叉树是n(n 0)个结点的有限集,它或者是空集(n=0),或由一个根结点及两棵互不相交的、分别称作这个根的左子树和右子树的二叉树构成。v特点l每个结点至多有两棵子树(即不存在度大于2的结点)l二叉树的子树有左、右之分,且其次序不能任意颠倒 二叉树并非是树的特殊情形,尽管二者有许多相同之处,但它们 是两种不同的数据结构。v基本形态A只有根结点的二叉树空二叉树AB右子树为空AB左子树为空ABC左、右子树均非空证明:用归纳法证明之 i=1时,只有一个根结点, 是对的 假设对所有j(1jBDCD L R先序遍历序列:A B D C先序(前序)遍历二叉树:ADBCL D RBL D RL D RADCL D R中序遍历序列:B D A C中序遍历二叉树:ADBC L R DL R DL R DADCL R D后序遍历序列: D B C A后序遍历二叉树:B-+/a*b-efcd先序遍历:中序遍历:后序遍历:层次遍历:- + a * b - c d / e f-+a*b-cd/ef- +a *b - c d/e f-+a*b-c d/e fDLRLDRLRDv算法(1)前序遍历算法 DLR void PreOrder(bitree *t) /*前序遍历二叉树*/ if (t!=NULL) printf(“t%cn”,t-data); /*打印根结点*/ PreOrder(t-lchild); /*前序遍历*t左子树*/ PreOrder(t-rchild); /*前序遍历*t右子树*/ (2)中序遍历算法 LDR void InOrder(bitree *t) /*中序遍历二叉树*/ if(t!=NULL) /*二叉树t非空*/ InOrder(t-lchild); /*中序遍历*t的左子树*/ printf(“/t%cn”,t-data); /*打印根结点*/ InOrder(t-rchild); /*中序遍历*t的右子树*/ (3)后序遍历算法 LRD PostOrder(bitree *t) /*后序遍历二叉树*/ if(t!=NULL) /*二叉树t非空*/ PostOrder(t-lchild); /*后序遍历*t的左子树*/ printf(“/t%cn”,t-data); /*打印根结点*/ PostOrder(t-rchild); /*后序遍历*t的右子树*/ 若去掉三中遍历算法中的打印输出语句,三种遍历算法 基本相同。说明这三种遍历的搜索路线相同。 该路线从根结点出发,逆时针沿着二叉树外缘移动,对每个结点均途径三次。(参教材Page97-98)void preorder(bitree *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);DTCprintf(C);pre(T L);C返回T左是空返回pre(T R);T左是空返回T右是空返回pre(T R);返回T左是空返回返回T右是空返回递归算法void inorder(bitree *bt) int i=0; bitree *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);非递归算法BApCDEFGip-A(1)ABCDEFGpip-Ap-B(2)ABCDEFGpip-Ap-B(3)p-Cp=NULLABCDEFGiP-AP-B访问:C(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 B E G D F Ap=NULL(15)v遍历算法应用l按先序遍历序列建立二叉树的二叉链表,已知先序序列为: A B C D E G F ABCDEFGbitree *crt_bt_pre(bitree *bt) char ch; printf(ch=); scanf(%c,&ch); getchar(); if(ch=#) bt=NULL; else bt=(bitree *)malloc(sizeof(bitree); bt-data=ch; bt-lchild=crt_bt_pre(bt-lchild); bt-rchild=crt_bt_pre(bt-rchild); return(bt); A B C D E F G l统计二叉树中叶子结点个数算法#include #include typedef struct node char data; struct node *lchild,*rchild;bitree;void countleaf(bitree *bt,int *count) if(bt!=NULL) if(bt-lchild=NULL)&(bt-rchild=NULL) (*count)+; return; countleaf(bt-lchild,count); countleaf(bt-rchild,count); void main() /* ABCDEGF */ bitree *head=NULL; int count=0; head=crt_bt_pre(head); countleaf(head,&count); printf(count of leaf node is %dn,count);v创建树并求二叉树的叶子结点算法l求二叉树深度算法void treedepth(bitree *bt,int *l,int *h) int l1=0,l2=0,h1=0,h2=0; if(bt!=NULL) (*l)+; if (*l*h) *h=*l; treedepth(bt-lchild,&l1,&h1); treedepth(bt-rchild,&l2,&h2); if (h1h2) *h=*h+h1; else *h=*h+h2; void main() /* ABCDEGF */ bitree *head=NULL; int level=0,high=0; head=crt_bt_pre(head); treedepth(head,&level,&high); printf(depth of tree is %dn,high);6.5 树和森林树、森林与二叉树的转换 在树或森林与二叉树之间有一个自然的一一对应关系。任何一个森林或一棵树可唯一地对应导一棵二叉树;反之,任何一棵二叉树也能唯一地对应到一个森林或一棵树。 A B C D E A B C D E A B C D E 存储存储解释解释v 树与二叉树转换v将树转换成二叉树l加线:在兄弟之间加一连线l抹线:对每个结点,除了其左孩子外,去除其与其余孩子之间的关系l旋转:以树的根结点为轴心,将整树顺时针转45ABCDEFGHIABCDEFGHIABCDEFGHIABCDEFGHIABCDEFGHI树转换成的二叉树其右子树一定为空v将二叉树转换成树l加线:若p结点是双亲结点的左孩子,则将p的右孩子,右孩子的右孩子,沿分支找到的所有右孩子,都与p的双亲用线连起来l抹线:抹掉原二叉树中双亲与右孩子之间的连线l调整:将结点按层次排列,形成树结构ABCDEFGHIABCDEFGHIABCDEFGHIABCDEFGHIABCDEFGHIv森林转换成二叉树l将各棵树分别转换成二叉树l将每棵树的根结点用线相连l以第一棵树根结点为二叉树的根,再以根结点为轴心,顺时针旋转, 构成二叉树型结构ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJv二叉树转换成森林l抹线:将二叉树中根结点与其右孩子连线,及沿右分支搜索到的所有右孩子间连线全部抹掉,使之变成孤立的二叉树l还原:将孤立的二叉树还原成树ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ6.6 二叉树的应用哈夫曼树(Huffman)带权路径长度最短的树v定义l路径:从树中一个结点到另一个结点之间的分支构成这两个结点间的l路径长度:路径上的分支数l树的路径长度:从树根到每一个结点的路径长度之和l树的带权路径长度:树中所有带权结点的路径长度之和lHuffman树设有n个权值w1,w2,wn,构造一棵有n个叶子结点 的二叉树,每个叶子的权值为wi,则wpl最小的二叉树叫例 有4个结点,权值分别为7,5,2,4,构造有4个叶子结点的二叉树dcab2475WPL=7*3+5*3+2*1+4*2=46abcd7524WPL=7*2+5*2+2*2+4*2=36abcd7524WPL=7*1+5*2+2*3+4*3=35其中(c)树的WPL最小,可以验证,它就是哈夫曼树。v构造Huffman树的方法Huffman算法l构造Huffman树步骤u根据给定的n个权值w1,w2,wn,构造n棵只有根结点的二叉树,令起权值为wju在森林中选取两棵根结点权值最小的树作左右子树,构造一棵新的二叉树,置新二叉树根结点权值为其左右子树根结点权值之和u在森林中删除这两棵树,同时将新得到的二叉树加入森林中u重复上述两步,直到只含一棵树为止,这棵树即哈夫曼树例a7b5c2d4a7b5c2d46a7b5c2d4611a7b5c2d461118例 w=5, 29, 7, 8, 14, 23, 3, 1151429 7823 3111429 7823 1135887151429233581111358191429238715113581929 23148715292914871529113581923421135819234229148715295811358192342291487152958100Huffman算法实现u一棵有n个叶子结点的Huffman树有2n-1个结点u采用顺序存储结构一维结构数组u结点类型定义#define M 50#define MAX 100typedef struct int data; int parent,lchild,rchild;hufmtree;typedef struct int data; int parent,lchild,rchild;hufmtree;void huffman(int n,int w,JD t) int i,j,k,x1,x2,m1,m2; for(i=1;i(2*n);i+) ti.pa=ti.lc=ti.rc=0; if(i=n) ti.data=wi; else ti.data=0; for(i=1;in;i+) m1=m2=MAX; x1=x2=0;for(j=1;j(n+i);j+) if(tj.datam1)&(tj.pa=0) m2=m1; x2=x1; m1=tj.data; x1=j; else if(tj.datam2)&(tj.pa=0) m2=tj.data; x2=j; k=n+i; tx1.pa=tx2.pa=k; tk.data=m1+m2; tk.lc=x1; tk.rc=x2; lc data rc pa1 2 3 4 5 6 70 0 0 0 0 0 07 5 2 4 0 0 00 0 0 0 0 0 00 0 0 0 0 0 0(1)lc data rc pa1 2 3 4 5 6 70 0 0 0 3 0 07 5 2 4 6 0 00 0 0 0 4 0 00 0 5 5 0 0 0kx1=3,x2=4m1=2,m2=4(2)lc data rc pa1 2 3 4 5 6 70 0 0 0 3 2 07 5 2 4 6 11 00 0 0 0 4 5 00 6 5 5 6 0 0kx1=2,x2=5m1=5,m2=6(3)lc data rc pa1 2 3 4 5 6 70 0 0 0 3 2 17 5 2 4 6 11 180 0 0 0 4 5 67 6 5 5 6 7 0kx1=1,x2=6m1=7,m2=11(4)vHuffman树应用l最佳判定树等级分数段比例ABCDE05960697079 8089 901000.050.150.400.300.10a60a90a80a70EYNDYNCYNBYNA70a80a60CYNBYNDYNEYNA80a9060a70EADECa80a70a60a90EYNDYNCYNBYNAlHuffman编码:数据通信用的二进制编码u思想:根据字符出现频率编码,使电文总长最短u编码:根据字符出现频率构造Huffman树,然后将树中结点引向其左孩子的分支标“0”,引向其右孩子的分支标“1”;每个字符的编码即为从根到每个叶子的路径上得到的0、1序列要传输的字符集 D=C,A,S,T, ; 字符出现频率 w=2,4,2,3,3CS3364224814T;AT : 00; : 01A : 10C : 110S : 111u译码:从Huffman树根开始,从待译码电文中逐位取码。若编码是“0”,则向左走;若编码是“1”,则向右走,一旦到达叶子结点,则译出一个字符;再重新从根出发,直到电文结束CS3364224814T;A00110110T : 00; : 01A : 10C : 110S : 111例 电文是CAS;CAT;SAT;AT 其编码 “ 电文为“1101000” 译文只能是“CAT”
收藏 下载该资源
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号