1、非线性数据结构(2)- 图,教学目标,了解有关图的 基本概念 存储结构及实现 遍历算法,教学要求,通过本单元学习,了解、掌握有关图: 基本概念 有向图、无向图、连通图、网 存储结构及实现 邻接矩阵、邻接表 遍历及其它操作 深度优先、广度优先遍历 应用,本单元涉及的内容,第2章 2.5图的逻辑结构及其运算 2.6图类 2.7图的遍历 2.8树和图的基本应用 P73P90,一、图及其基本概念,图是一种较之线性表和树形结构更为复杂的非线性数据结构。图中各数据元素之间的关系可以是任意的,描述的是“多对多”的关系。 图的定义 有向图、无向图 图的基本概念 邻接点、顶点、边、弧、顶点的度 连通图、强连通图
2、、连通分量 网、权,图结构,图是对结点的前趋和后继个数不加限制的数据结构。有关图的理论,在“离散数学”的图论中有详细论述和证明。在DS中,只讨论图在计算机中的实现和操作。 现实生活中,图的应用范围很广泛,涉及: 电讯工程、电网调度、集成电路设计 交通管理、工程管理、系统工程等领域,图(Graph)的定义,图G = (V,E )其中: V= v1,v2,,vn是非空有穷的结点集合;E 是顶点偶对的集合。 例,图G1 = (V,E)V=v1,v2,v3,v4E=(v1,v2),(v1,v3),(v2,v1),(v2,v3),(v2,v4),(v3,v1),(v3,v2),(v4,v2),o,o,o
3、,o,v1,v2,v3,v4,G1,有向图、无向图,有向图(Digraph)图G中顶点的偶对若是有向的,形成的图称有向图。如图G2所示。为示区别,其偶对用表示。 无向图(Undigraph)图G中顶点的偶对若是无向的,形成的图称为无向图,其偶对用(vx,vy)表示,如图G1所示。 G2=(V,E)V= 1,2,3,4E=1,2,1,3,3,4,4,1,1,3,2,4,G2,边、弧,边(Edge)顶点间的关系可描述为顶点的偶对,也称为顶点的边。记为: (Vx,Vy)。边是无序的,可以看成是(Vx,Vy),也可以看成是(Vy,Vx)。 弧(Arc)若顶点间的边是有方向性(有序)的,则称该偶对为弧。
4、记为:Vx,Vy。弧是有序的,Vx,Vy表示从Vx到Vy。 弧头(Head)弧的终点(TerminaL Node)称为弧头(方向前方)。 弧尾(Tail)弧的起始点(Initial Node)称为弧尾(方向后方)。弧 Vx,Vy表示为, Vx Vy,弧尾 弧头,顶点、邻接点,顶点(Vertex) 图中的数据元素(结点)称为顶点。如图G1、G2中的1、2,1,2。 邻接点(Adjacent) 无向图中,若边(x,y) E,则x、y互为邻接点。 有向图中,若弧x,y E,则y是x的邻接点,反之,不是。,Vx,Vy,x、y互为邻接点,Vx,Vy,y是x的邻接点,1,3,2,4,G2,o,o,o,o,
5、v1,v2,v3,v4,G1,顶点的度(Degree),无向图中,顶点的度是以该顶点为一个端点的边的条数。例如,G1中V2的度为3,V4的度为1。 有向图中,以某顶点为弧头的弧的数目称为该顶点的入度(Indegree)。例如G2中顶点1的入度为1。以某顶点为弧尾的弧的数目称为该顶点的出度(Outdegree)。例如G2中顶点1的出度为2。该顶点的度=入度+出度。例如,G2中顶点1的度=2+1=3。,o,o,o,o,v1,v2,v3,v4,G1,1,3,2,4,G2,路径、长度,路径(Path)在图中,从顶点Vx到顶点Vy的顶点序列(Vx,V1,V2,,Vn,Vy)称为从Vx到Vy的路径。路径可
6、能是不唯一的。例如,G1中,V1到V3的路径为:(V1V2V3)或(V1V3);而G2中,1到4的路径为。 长度(Length)路径的长度是该路径上边或弧的数目。例如,G1中V1到V3的长度为1或2;而G2中1到4的长度为2。,1,3,2,4,G2,o,o,o,o,v1,v2,v3,v4,G1,连通图、强连通图、连通分量,1,2,连通图(Connected Graph)在无向图中,若每一对顶点间都有路径,称此图是连通图。如图G3所示。 连通分量(Connected Component) 无向图中的极大连通子图称为连通分量。 强连通图(Strongly Connected Graph)在有向图中
7、,若每对顶点Vx到Vy 间都存在Vx到Vy,及从Vy到Vx的路径,则称此图是强连通图。如图G4所示。,3,4,5,G3,2,1,3,4,5,G4,子图、生成树,子图有两个图G和G1,若V1V,E1 E,即V1中的顶点G = (V,E) 都属于V中的顶点,E1中的关系都G1 =(V1,E1) 属于E中的关系,则称G1是G的子图。 生成树设G是一个连通图,G中任一包含G的所有顶点的子图称为生成子图。如果该子图是树,则称为G的生成树。G的生成树不是唯一的。一棵有n个顶点的生成树,有且仅有n-1条边(弧)。,网、权,权(Weight)若图的边或弧带有与之相关的数,称此数为该边或弧的权。权通常用来表示从
8、一个顶点到另一个顶点的距离或费用。 网(Network)带权的图称为网。如G5为带权的网。,V1,V2,V3,V4,G5,2,3,5,7,二、图的存储结构,前面在讨论树和线性表的存储结构时,用到两种存储结构:顺序表和链表。但图结构中的结点间没有确定的关系(没根),任意两点之间都可能存在联系,因此无法用顺序结构来存放图的顶点数据。但借助数组可以用来表示顶点之间的关系。实际上,在图的存储结构中,常用下面两种方法: 邻接矩阵表示法 邻接表表示法,1、邻接矩阵表示法,根据图的定义可知,图的逻辑结构分为两部分:V和E的集合。因此, 用一个一维数组存放图中所有顶点数据; 用一个二维数组存放顶点间关系(边或
9、弧)的数据,称这个二维数组为邻接矩阵。 邻接矩阵又分为有向图邻接矩阵和无向图邻接矩阵。,有向图邻接矩阵,定义设图G=(V,E)是有n(n 1)个顶点的图,则G的邻接矩阵是具有下述性质的nxn的方阵,元素为:1 当Vi,Vj E 时A i,j = 0 当Vi,Vj E 时例如,G2的邻接矩阵为:,G.nodes=,1 2 3 4,A= G.Arc=,0 1 1 00 0 0 00 0 0 11 0 0 0,4x4,1,3,2,4,G2,无向图邻接矩阵,定义设图G=(V,E)是有n(n 1)个顶点的图,则G的邻接矩阵是具有下述性质的对称阵,元素为:1 当(Vi,Vj) E 时A i,j =Aj,i
10、 = 0 当(Vi,Vj) E 时例如,G1的邻接矩阵为:,G.nodes =,1 2 3 4,A=G.edge =,0 1 1 01 0 1 11 1 0 00 1 0 0,4x4,o,o,o,o,v1,v2,v3,v4,G1,求图中顶点的度,借助邻接矩阵,可以很容易地求出图中顶点的度。 无向图 邻接矩阵的第i行(或第i列)的元素之和是顶点Vi的度。例,G1中V2的度是3。 有向图 邻接矩阵第i行的元素之和为顶点Vi的出度;第j列的元素之和为顶点Vj的入度。例,G2中,V2的出度为0(第2行的元素之和为0),V1的入度为1(第1列的元素之和为1)。,o,o,o,o,v1,v2,v3,v4,G
11、1,A= G.Arc=,0 1 1 00 0 0 00 0 0 11 0 0 0,1,3,2,4,G2,网的邻接矩阵,定义:Wij 若(Vi,Vj)或Vi,Vj EAi,j = 若(Vi,Vj)或Vi,Vj E,G.nodes=,V1 V2 V3 V4,A=G.Arc=, 5 3 2 7 ,4x4,G5的邻接矩阵。,V1,V2,V3,V4,G5,2,3,5,7,2、邻接表表示法,邻接表是图的一种链式存储结构。对图的每个顶点建立一个单链表(n个顶点建立n个单链表),第i个单链表中的结点包含顶点Vi的所有邻接顶点。 在邻接表中,每个顶点由三个域组成:每个单链表附设一个头结点,结构为:,adjvex
12、 data nextarc,顶点Vi的邻接点,与边或弧有关的权值,指向Vi的下一个 邻接点的指针,Vexdata firstarc,指向Vi单链表的第一个结点,存放Vi信息,邻接表存储结构描述,C语言描述#define VTXNUM nstruct arcnode int adjvex;float data;struct arcnode *nextarc;typedef struct arcnode ARCNODE ;struct headnode int data ;ARCNODE * firstarc ; adjlistVTXNUM;,无向图G1的邻接表,V1V2V3V4,V3,V2,V1
13、,V4,V3,V1,V2,V2,顶点Vi的度恰好就是 第i个单链表中的结点数。,o,o,o,o,v1,v2,v3,v4,G1,有向图G2的邻接表,1234,2,3,4,1,在有向图中,第i个单链表中结点的个数是顶点Vi的出度;例如,V2的出度为0。 为求入度,必须遍历整个邻接表。,1,3,2,4,G2,逆邻接表,为求顶点Vi的入度,对每个顶点Vi,建立一个链接以Vi为弧头的邻接点链表,称该表为逆邻接表。例如G2的逆邻接表为:,1,2,3,4,4,1,1,3,显然逆邻接表的单链表中结点的个数就是顶点Vi的入度。,1,3,2,4,G2,建立邻接表的算法,操作步骤: step1 初始化邻接表的n个头
14、结点,并读入一条弧或边的偶对i,j或(i,j)。 step2 申请一个结点S的空间,将S插入到第i个单链表中; step3 读下一条弧或边的偶对,若存在此弧或边,则继续执行step2;否则,结束。,建立邻接表算法的程序,createadjlist(struct headnode G,int n) int i,j,k; float w; ARCNODE *s;for (k=1; k=0 ,三、图的遍历,图的遍历(Traversing Graph)从图中指定顶点出发访问图中每一个顶点,且使每个顶点只被访问一次,该过程为图的遍历。 图的遍历要比树结构复杂的多。出发点不同,任一顶点的邻接点就不相同,路
15、径也就不同。 因为图中元素是“多对多”的关系,为避免发生重复,设立一个辅助数组visited,每访问一个顶点,便将其状态visitedi置为“真”。 常用的图的遍历方法有两种: 深度优先遍历法 广度优先遍历法,深度优先遍历法,深度优先遍历法类似于树的先根遍历法。 算法思想: step1 从图中某个顶点V0出发,并访问此顶点; step2 从V0出发,访问与V0邻接的顶点V1后,再从V1出发,访问与V1邻接且未被访问过的顶点V2。重复上述过程,直到不存在未访问过的邻接点为止。 step3 如果是连通图,从任一顶点V0出发,就可以遍历所有相邻接的顶点;如果是非连通图,则再选择一个未被访问过的顶点作
16、为出发点,重复step1、step2,直到全部被访问过的邻接点都被访问为止。,深度优先遍历法举例,遍历过程 访问顶点 所过边,起始顶点V1 V1 V1的第1个邻接点V3 V3 (V1,V3) V3的第1个邻接点V1已访问,取下一个邻接点V5 V5 (V3,V5) V5的第1个邻接点V3已访问,取下一个邻接点V2 V2 (V5,V2) V2的两个邻接点均被访问,回退到V5,V5的邻接点均被访问,回退到V3,V3的邻接点均被访问 ,回退到V1,V1的另一个邻接点V4未被访问 V4 (V1,V4) V4的第一个邻接点V1已被访问,另一个邻接点V6未被访问 V6 (V4,V6) V6的邻接点被访问,回
17、退到V4 V4的邻接点均被访问 回退到V1,返回到出发点,遍历结束。,V1,V3,V5,V4,V6,G6,V2,示例,遍历产生的结果,深度优先遍历G6所走过的序列:V1 V3 V5 V2 V4 V6 所走过的边:(V1,V3),(V3,V5),(V5,V2),(V1,V4),(V4,V6) 遍历生成树,V1,V3,V5,V2,V4,V6,深度优先遍历算法程序(非递归),Traver_dfs(struct headnode G,int v) int i,stackN,visitedN, top=-1 ; ARCNODE *p;for (i=0;i”,Gv.data);visitedv=1; to
18、p +; stacktop=v; p=Gv.firstarc;while(top!=-1)|(p!=NULL) while(p!=NULL) if (visitedp-adjvex) p=p-nextarc;else printf(“%d -”,Gp-adjvex.data);visitedp-adjvex=1; top+; stacktop=p-adjvex;p=Gp-adjvex.firstarc; if( top !=-1) v=stacktop; top- -; p=Gv.firstarc;p=p-nextarc; ,广度优先遍历算法,广度优先遍历法类似于树的按层次遍历的过程。即先访问
19、第1个顶点所有邻接点后,再访问下一个顶点所有未被访问的邻接点。 算法思想: step1 从图中某个顶点V0出发,并访问此顶点; step2 从V0出发,访问V0的各个未曾访问的邻接点W1,W2,,Wk;然后,依此从W1,W2,Wk出发访问各自未被访问的邻接点。 step3 重复step2,直到全部顶点都被访问为止。,广度优先遍历法举例,遍历过程 访问顶点 所过边,起始顶点V1 V1 访问V1的未被访问过的所有邻接点 V3,V2,V4 (V1,V3)(V1,V2)(V1,V4) 访问V3的未被访问过的所有的邻接点 V5 (V3,V5) 访问V2的未被访问过的所有的邻接点 无 访问V4的未被访问过
20、的所有的邻接点 V6 (V4,V6) 所有顶点已被访问,结束。,V1,V3,V5,V4,V6,G6,V2,示例,遍历产生的结果,广度优先遍历G6所走过的序列:V1 V3 V2 V4 V5 V6 所走过的边:(V1,V3),(V1,V2),(V1,V4),(V3,V5),(V4,V6) 遍历生成树,V1,V3,V5,V2,V4,V6,四、图的操作,图中顶点无序可言 任一顶点都可以看作第一个顶点; 任一顶点的邻接点间也无序可言; 为操作方便,对图中顶点按人为意志给其排序;同样,也可以对某个顶点的所有邻接点进行排队,在这个队列中自然形成第一个或第K个邻接点。若某个顶点的邻接点个数大于K,则称第K+1
21、个邻接点为第K个邻接点的下一个邻接点,而最后一个邻接点的下一个邻接点为“空”。,图的常用基本操作,LOC_VERTEX(G,Vi)确定顶点Vi在G中的位置。 GET_VERTEX(G,i)求图G中第i个顶点。 FIRST_VERTEX(G,i)求图G中Vi的第1个邻接点。 NEXT_ADJ(G,v,w) 已知w为G中顶点v的第1个邻接点,求顶点w的下一个邻接点。 INS_VERTEX(G,u) 在图G中插入一个顶点u,为图G的第n+1个顶点。 INS_ARC(G,v,w) 在图G中插入一条从顶点v到顶点w的弧。 DEL_VERTEX(G,Vi) 删除图G中顶点Vi,以及与Vi相关联的边或弧。
22、DEL_ARC(G,v,w) 删除图G中从顶点v到顶点w的弧。,五、图的应用,最小生成树 拓扑排序 关键路径 最短路径,最小生成树,该问题是构造连通图的最小代价生成树问题。一棵生成树的代价就是树上各边(弧)的代价之和。 例如,若要在n个城市间建立通信联络网,则只需n-1条线路。但在n个城市间,最多可能架设n(n-1)/2条线路,选择哪n-1条线路,使费用最少。 普里姆(Prim)算法 克鲁斯卡尔(Kruskal)算法,普里姆(Prim)算法,假设N=(V,E)是连通图,TE是N上最小生成树中边的集合。 从U=u0 (u0 V),TE= 空开始; 重复执行: 在所有uU,v V-U的边(u,v)
23、E中找一条代价最小的边(u0,v0)并入TE,同时u0并入U,直到U=V为止; 此时TE中必有n-1条边,则T=(V,TE)为N的最小生成树。,普里姆(Prim)算法举例,1,2,3,4,5,6,8,7,2,1,4,3,5,7,6,8,11,10,9,12,U=1,V-U=2,3,4,5,6,7,8,1,2,2,1,2,4,2,1,4,7,3,(1),(2),(3),(1) U=1,2,V-U=3,4,5,6,7,8 (2) U=1,2,4,V-U=3,5,6,7,8 (3) U=1,2,4,7,V-U=3,5,6,8,普里姆(Prim)算法举例(续),4,7,5,3,5,(4),7,6,(5
24、),6,(6),4,7,6,3,8,(7),8,3,6,5,4,7,2,2,1,3,6,5,8,9,(4) U=1,2,4,7,5 , V-U=3,6,8(5) U=1,2,4,7,5,6, V-U=3,8(6) U=1,2,4,7,5,6,3, V-U=8(7) U=1,2,4,7,5,6,3,8), V-U= ,4,3,6,3,1,克鲁斯卡尔(Kruskal)算法,操作步骤: 假设N=(V,E)是连通图 取图中每个顶点自成一个连通分量 在 E 中选择代价最小的边,若该边所依附的顶点落在T中不同的连通分量上,则将此边加入生成树T中;否则,舍去此边,再选择下一条代价最小的边。 重复上述步,直到
25、T中所有顶点都在同一连通分量上为止。,克鲁斯卡尔(Kruskal)算法举例,1,2,3,4,5,6,1,5,2,4,6,6,3,5,5,6,克鲁斯卡尔(Kruskal)算法举例(续),1,2,3,4,5,6,(5),1,2,3,4,1,2,3,4,5,6,1,5,2,4,6,6,3,5,5,6,拓扑排序,研究一个有机整体中不同个体间的次序问题。例如课程间优先关系有向图问题。 软件专业的课程优先关系:课程编号 课程名称 先决条件C1 程序设计基础 无C2 离散数学 C1C3 汇编语言 C1,C2C9 高等数学 无,拓扑排序举例,方法步骤 在有向图中选一个没有前驱的顶点并输出; 从图中删除该顶点和
26、所有以它为尾的弧; 重复前2步,直到全部顶点均输出为止。 输出序列即为拓扑排序序列。 拓扑排序序列不唯一。,拓扑排序举例,C1 C2 C3 C4 C5 C7 C9 C10 C11 C6 C12 C8 C9 C10 C11 C6 C1 C12 C4 C2 C3 C5 C7 C8,关键路径,研究的是在一个有机整体中不同个体间的次序问题。即研究的是工程进度及影响进度的关键因素问题。,最短路径,研究的是类似于交通调度一类的带权有向图问题。求路径最短或费用最省。,六、二叉排序树的生成,对给定数列a1,a2,,an,根据二叉排序树特性,逐个将结点插入到二叉排序中。 具体操作步骤:step1 a1是根结点;
27、step2 若aiaj ,且aj的左子为空,则将ai插入到aj的左子;若aj的左子非空,则继续寻找合适的插入位置。若aiaj,且aj的右子为空,则将ai插入到aj的右子;否则继续沿右子树寻找合适的插入位置;插入ai 插入到aj的左子 aiaj插入到aj的右子 ai ajstep3 重复step2,直到所有结点插入完为止。,二叉排序树算法,二叉排序树结点结构:struct tree char info;struct tree *left , * right ;算法描述: 输入结点非空循环, 若是第1个结点,令指针ROOT指向该结点。 打印输出该二叉排序树; 输入一个值,在该树中查找,若找到输出该
28、结点值;否则,显示查找失败。,二叉排序树生成算法,主程序框图,开始,初始化,输入结点数据,!ROOT,root=create_btee(),create_btree(),结束?,N,Y,打印该树,查找指定结点,Print_btree(),Search_btree(),生成二叉排序树程序框图,Create_btree(),开始,r = 0?,Y,申请结点空间,r = 0?,显示“溢出”,结束,根结点的处理,N,Y,N,r-left = 0; r-right = 0; r-info = info ;,非根结点,infoinfo?,Y,t=r-left,t=r-right,N,调用本函数,root?
29、,返回,Y,N,r-left=0 r-right=0,root-left=r 或 root-right=r,查询二叉排序树算法框图,开始,Search_btree(),!root,Y,显示“空树”,返回,N,root-info!=key循环,keyinfo?,root=root-left,root=root-right,root=0?,N,Y,查找成功,显示,返回,显示“失败”,root!=0?,循环结束?,N,Y,打印算法框图,开始,Print_btree(),r=0?,Y,N,调用自身打印左子,打印当前结点值,调用自身打印右子,返回,主程序Btree.C,#include “stdio.h
30、” struct tree char info; struct tree *left,*right; main ( ) char *s,*c,key=; struct tree *create_btree(),*search_btree(),*root=0; do printf(“Enter a letter:”); gets(s);if (!root)root=create_btree(root,root,*s);elsecreate_btrr(root,root,*s); while (*s) ;,主程序Btree.C(续),print_btree(root,0);key=1;while
31、( key)printf(“Enter a key to find:”);scanf(“%s”, /* Btree.C 结束 */,生成二叉排序树程序,struct tree create_btree(root,r,info)struct tree *root,*r;char info; if (r = =0 ) r=malloc(sizeof(struct tree);if ( r = = 0) printf(“Out of memoryn”); exit(0); r-left= 0; r-right=0; r-info=info;if (root) if(infoinfo) root -
32、left=r;else root-right=r;else r-right=0; r-left = 0; return r; /* if = = 0 接下页 */,生成二叉排序树程序(续),if (info info)create_btree(r,r-left,info);if(info=r-info)create_btree(r,r-right,info); /* create_btree(root,r,info) */,struct tree *search_btree(root,key)struct tree *root; char key; if (!root) printf(“Emp
33、tu btreen”); return root; while(root-info!=key) if(keyinfo) root=root-left;elseroot=root-right;if(root=0) printf(“Search Failuren”);break ; /* while(root-info!=key) */,查询二叉排序树程序,if (root !=0)printf(“Successful searchn key=%cn”,root-info);return root ; /* *search_btree(root,key) */,查询二叉排序树程序(续),打印二叉排
34、序树程序,print_btree(r,l) struct tree *r; int l; int i;if (r = = 0) return ;print_tree(r-left,l+1);for(i=0;iinfo);print_btree(r-right,l+1); ,程序输入,输入: 输出:h bd dp er hb p e r,举例,对数列10,18,3,4,9,13,25,生成二叉排序树。,示例,平衡二叉排序树,在二叉排序树的动态生成过程中,由于数据本身的特性,将影响二叉排序树的性质,例如3,5,7,9,20这样的数列,生成的二叉排序树就是一棵单枝树。因此,在动态生成二叉排序树的过程
35、中,要进行平衡化处理。平衡化处理就是在不影响二叉排序树特性的前题下,通过“旋转”处理,使该结点的平衡因子不大于2。旋转分:LL、RR、LR和RL四种。,3,5,7,9,20,LL平衡化处理,由于在A的左子树的左子树上插入结点,使A点失去平衡,需进行一次LL旋转(顺时针旋转)操作。 程序实现为:b = a.lca.lc = b.rcb.rc = aa.bf = 0b.bf = 0b为子树的新根,A,B,C,LL,B,C,A,示例,示意为:,RR平衡化处理,由于在A的右子树的右子树上插入结点,使A点失去平衡,需进行一次RR旋转(逆时针旋转)操作。 程序实现为:b = a.rca.rc = b.lc
36、b.lc = aa.bf = 0b.bf = 0b为子树新根,A,B,C,RR,B,A,C,示例,示意为:,LR平衡化处理,程序实现为:b = a.lc、 c = b.rc、 a.lc = c.rcb.rc = c.lc、 c.lc = b、 c.rc = a,A,B,C,C,B,A,A,C,B,示例,由于在A的左子树的右子树上插入结点,使A点失去平衡,需进行一次LR旋转(两次旋转;先逆时针,再顺时针旋转)操作。示意为:,RL平衡化处理,由于在A的右子树的左子树上插入结点,使A点失去平衡,需进行一次RL旋转(两次旋转;先顺时针,再逆时针旋转)操作。,A,B,C,C,A,B,A,C,B,程序实现
37、为:b = a.rc、 c = b.lc、 a.rc = c.lcb.lc = c.rc、 c.lc = a、 c.rc = b,示意为:,示例,平衡化处理举例,有数据序列13,24,37,90,53 插入13,树是平衡的; 插入24,树仍为平衡; 插入37,树不再平衡,执行RR旋转; 插入90,树仍平衡; 插入53,失去平衡,执行RL旋转.,示例,Huffman(哈夫曼)树及应用,Huffman树的定义 构造Huffman树 Huffman编码 Huffman编码的译码,Huffman树的定义,Huffman树也称为最优树,是一类带权路径最短的二叉树。 树的带权路径长度定义为:,WPL =
38、wklk,k = 1,n,其中: n 是树中叶结点的个数wi 是第i个结点的权值li 是第i个结点的路径长度,Huffman树举例,以下有三棵树:,(a),(b),(c),a,b,c,d,a,b,c,d,a,c,b,d,7,7,7,5,5,5,2,2,2,4,4,4,WPLa =7x2+5x2+2x2+4x2= 36,WPLb =7x3+5x3+2x1+4x2= 46,WPLc =7x1+5x2+2x3+4x3= 35 ,事实证明按哈夫曼树构造二叉树,可得到很好的特性,应用于实际问题,可提高处理效率。,应用举例,由统计规律可知,考试成绩的分布符合正态分布:,-1,1,0,分数 059 60 6
39、9 70 79 80 89 90 100,比例数 0.05 0.15 0.40 0.3 0.10,根据正态分布规律,在60 90之间的分数占85%,而不及格和优秀是少数。,将百分制转换成五分制,判定树比较:,a60?,a70?,a80?,a90?,不及格,及格,中等,良好,优秀,Y,Y,Y,Y,N,N,N,N,a80?,a70?,a90?,a60?,不及格,优秀,良好,中等,中等,及格,不及格,Y,Y,Y,N,N,N,N,Y,Y,(A),(B),若输入1万个数据,按A的判定过程进行操作,约需比较3.2万次,而按B比较,则仅需2.2万次。,构造Huffman树,构造Huffman树算法步骤: 1
40、)将n个带权值wi(in)的结点构成n棵二叉树的集合T=T1,T2,Tn,每棵二叉树只有一个根结点。 2)在T中选取两个权值最小的结点作为左右子树,构成一个新的二叉树,其根结点的权值取左右子树权值之和; 3)在T中删除这两棵树,将新构成的树加入到T中; 4)重复2)、3)步的操作,直到T中只含一棵树为止,该树就是Huffman树。,构造Huffman树举例,以权值分别为7,5,2,4的结点a、b、c、d构造Huffman树。T= a b c d ,(d)T= T1 ,(c)T= a T2 ,(b)T= a b T3 ,(a)T= a b c d ,示例,Huffman编码,编码:用二进制数的不
41、同组合来表示字符的方法。 前缀编码:一种非等长度的编码(任一个字符的编码都不是另一个字符编码的前缀)。,a,0,b,0,1,c,d,0,1,1,编码:A(0)B(01)C(011)D(111),方法约定: 1)左分支为0 2)右分支为1 3)由叶到根路径上字符组成的二进制串就是该叶结点的编码。,Huffman编码:一种非等长度的编码。以给定权值的结点构造Huffman树,按二进制前缀编码的方式构成的编码为Huffman编码。,Huffman编码举例,在某系统的通信联络中可能出现8种字符,其频率分别为0.05、0.29、0.07、0.08、0.14、0.23、0.03、0.11,设权值分别为5,
42、29,7,8,14,23,3,11,n=8,其Huffman树为:,0,0,0,0,0,0,0,1,1,1,1,1,1,1,5,3,7,8,14,29,11,23,42,58,100,Huffman编码为: A 5 0110 B 29 01 C 7 0111 D 8 1111 E 14 011 F 23 00 G 3 1110 H 11 010,Huffman编码存储结构,由于Huffman树中没有度为1的结点,则n个叶结点的Huffman树共有2n-1个结点。例如,4个结点的Huffman树,共有7个结点。因此可以用长度为2n-1的一维数组存放。 求Huffman编码: 从叶到根的编码。因此
43、要知道每个结点的父结点。,0,0,0,0,0,0,0,1,1,1,1,1,1,1,5,3,7,8,14,29,11,23,42,58,100,Huffman编码为: A 5 0110 B 29 01 C 7 0111 D 8 1111 E 14 011 F 23 00 G 3 1110 H 11 010,Huffman编码的译码,从Huffman编码树上不难看出,代码全部在叶结点上,根据Huffman编码,就能求出相应的字符。该过程称为“译码”。 译码是根据从根到叶的Huffman编码求相应的字符。因此要知道每个结点的左右子结点。 例如,根据“1111”,就能求出对应的字符是“8”。,0,0,0,0,0,0,0,1,1,1,1,1,1,1,5,3,7,8,14,29,11,23,42,58,100,作业题,P8913、18、20,