首页计算机计算机基础数据结构与算法 06 第6章 树和二叉树.pptx

06 第6章 树和二叉树.pptx

页数:90页文件大小:914.2 KB来源: 数据结构与算法ppt课件

本课件讲解树的定义与基本术语,展开二叉树的定义性质与顺序、链式存储,重点给出先序中序后序遍历的递归与非递归算法及中序线索化,随后说明树和森林与二叉树的相互转换,最后讲赫夫曼树的构造与赫夫曼编码。

第六章树和二叉树章节封面页,标示本章讲树的定义、二叉树性质与存储、遍历、森林转换与赫夫曼树
树形结构概述页,说明树是以分支关系定义的层次结构,可用于形象表示家谱与单位部门
内容提要页,列出树和二叉树的存储结构与操作算法、树和森林与二叉树的关系等各节安排
查看本课件的文字版(共 90 页,供检索与复制)

第1页:第六章树和二叉树

第六章树和二叉树

第2页:树的概述

有关树的概述

  • 树形结构是非线性结构,以树和二叉树最为常用
  • 树是以分支关系定义的层次结构,例如:家谱、单位部门都可以用树来形象表示

第3页:第六章内容提要

本章目的:树和二叉树存储结构与操作算法、树和森林与二叉树的关系

  • 6.1树的定义和基本术语
  • 6.2二叉树
  • 6.3遍历二叉树和线索二叉树
  • 6.4树和森林
  • 6.6赫夫曼树及其应用
  • 本章重点本章内容:

第4页:第一节树的定义和基本术语

第一节树的定义和基本术语

第5页:树的定义

(1)树的定义

  • 树(Tree)是n(n≥0)个结点的有限集.在任何一棵非空树中
  • 有且仅有一个特定的称为根的结点
  • 当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1,T2,…, Tm,每个集合本身又是一棵树,并称为根的子树ABCDEKLFGHIJM(a)只有根结点的树(b)一般树1层2层3层4层A

第6页:ADTTree的定义

(2)抽象数据类型树的定义

  • ADTTree{
  • 数据对象D:D是具有相同特性的数据元素的集合。
  • 数据关系R:若D为空集,则称为空树;0若D仅含有一个元素,则R为空集,否则R={H},H是如下二元关系:
  • (1)在D中存在唯一的称为根的数据元素root,它在关系H下无前驱;
  • (2)若D-{root}≠Ø,则存在D-{root}的一个划分D1,D2,…,Dm(m>0),对任意j≠k(1≤j,k≤m)有Dj∩Dk=Ø,且对任意的i(1≤i≤m),唯一存在数据元素xi∈Di,有<root,xi>∈H;ABCDEKLFGHIJMAD-{root}=ØD-{root}≠Ø

第7页:ADTTree续:子树划分

(3)对应于D-{root}的划分,H-{< root, x1>,

  • ,< root,xm>}有唯一的一个划分H1,H2,
  • Hm(m>0)对任意j≠k(1≤k(1≤j,k≤m)有Hj∩Hk=Ø,且对任意i(1≤i≤m),Hi是Di上的二元关系(Di,{Hi})是一棵符合本定义的树,称为根root的子树
  • 基本操作:
  • InitTree(&T);

    第8页:树的基本术语

    (3)树的基本术语

    • 结点(node):表示树中的元素,包括数据项及若干指向其子树的分支
    • 结点的度:结点拥有的子树数
    • 叶子或终端结点:度为0的结点
    • 非终端结点或分支结点:度不为0的结点
    • 树的度:树内各结点的度的最大值
    • 结点的子树的根称为该结点的孩子,相应地该结点称为孩子的双亲
    • 结点的祖先是从根到该结点所经分支上的所有结点
    • 同一双亲的孩子之间互称兄弟

    第9页:树的层次与森林

    以某结点为根的子树中的任一结点都称为该结点的子孙

    • 结点的层次(level)——从根结点算起,根为第一层,它的孩子为第二层……
    • 双亲在一层次的结点互为堂兄弟
    • 树的深度(高度):树中结点的最大层次数
    • 如果树中结点的各子树看成是从左到右是有次序的(不能互换),则该树为有序树,否则称为无序树
    • 森林(forest)——m(m0)棵互不相交的树的集合

    第10页:第二节二叉树

    第二节二叉树

    第11页:二叉树的定义

    (1)二叉树定义

    • 二叉树是每个结点最多只有两棵子树的树,子树有左右之分,不能颠倒
    • 二叉树的基本形态(5种)

    第12页:ADTBinaryTree的定义

    (2)抽象数据类型二叉树

    • ADTBinaryTree{
    • 数据对象D:D是具有相同特性的数据元素的集合
    • 数据关系R:
    • 若D=Ø,则R=Ø,称BinaryTree为空二叉树
    • 若D≠Ø,则R={H},H是如下二元关系
    • (1)在D中存在唯一的称为根的数据元素root,它在关系H下无前驱;
    • (2)若D-|root|≠Ø,则存在D-{root}={Dl,Dr}且Dl∩Dr=Ø;
    • (3)若Dl≠Ø,则Dl中存在唯一的元素xl,<root,xl>∈H,且存在Dl上的关系Hl∈H;若Dr≠Ø,则Dr中存在唯一的元素xr,<root,xr>∈H,且存在Dr上的关系,Hr∈H;H={<root,x1>,<root,xr>,Hl,Hr};
    • (4)(Dl, {Hl})是一棵符合本定义的二叉树,称为根的左子树,(Dr,{Hr})是一棵符合本定义的二叉树,称为根的右子树

    第13页:二叉树的基本操作

    基本操作:

    • InitBiTree(&T);DestroyBiTree(&T);
    • BiTreeEmpty(T);BiTreeDepth(T);
    • Parent(T,e);LeftChild(T,e);RightSibling(T,e);
    • DeleteChild(&T,p,LR);
    • PreOrderTraverse(T,visit());InOrderTraverse(T,visit());
    • PostOrderTraverse(T,visit());LevelOrderTraverse(T,visit());
    • ……
    • }ADTBinaryTree

    第14页:二叉树的性质1

    (3)二叉树的性质

    • 性质1在二叉树的第i层上至多有2i-1个结点(i≥1)证明:用归纳法证明之G)i=1时,只有一个根结点,2i-1=20=1是对的;@假设对所有j(1j<i)命题成立,即第j层上至多有个结点2j-1,那么,第i-1层至多有2(i-1)-1=2i-2个结点;∵二叉树每个结点的度至多为2∴第i层上最大结点数是第i-1层的2倍(2*2i-2=2i-1),即故命题得证。123456789101112131415

    第15页:二叉树的性质2

    性质2深度为k的二叉树至多有2k-1个结点(k≥1)证明:由性质1,可得深度为k的二叉树最大结点数是(3)二叉树的性质(续)123456789101112

    第16页:二叉树的性质3

    性质3对任何一棵二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1证明:设n1为二叉树T中度为1的结点数,则二叉树中结点总数为: n=n0+n1+n2∵二叉树中,除根结点外,其余结点都只有一个分支进入,设B为分支总数,则n=B+1又∵分支由度为1和度为2的结点射出,B=n1+2n2于是,n=B+1=n1+2n2+1=n0+n1+n2n0=n2+1(3)二叉树的性质(续)

    第17页:满二叉树与完全二叉树

    (3)二叉树的性质(续)241132367567458910111213141589101112

    • 满二叉树:深度为k的二叉树有2k-1个结点
    • 完全二叉树:编号与满二叉树编号一一对应
    • 完全二叉树特点:
    • 叶子结点只可能在层次最大的两层上出现
    • 对于任意结点,若右分支下的子孙的最大层次为l,则左分支的子孙的最大层次必为l或l+11234567123456

    第18页:二叉树的性质4

    性质4具有n个节点的完全二叉树的深度为[log2n]+1证明:假设深度为k,则根据性质2和完全二叉树的定义有:2k-1-1< n≦2k-1则k-1≦lon2n<kk=log2n+1或2k-1≦n<2k(3)二叉树的性质(续)

    第19页:二叉树的性质5

    性质5如果对一棵有n个结点的完全二叉树(其深度为[log2n]+1)的结点按层序编号(从第1层到第[log2n]+1层,每层从左到右),则对任一结点i(1≤i≤n)有

    • (1)如果i=1,则结点i是二叉树的根,无双亲;如果i>1,则其双亲PARENT(i)是结点[i/2](例如i=2,3,4,5,…,12)
    • (2)如果2i>n,则结点i无左孩子(结点i为叶子结点)(例如i=7);否则其左孩子LCHILD(i)是结点2i(例如i=1,2,3,4,5,6)
    • (3)如果2i+1>n,则结点i无右孩子(例如i=6,7);否则其右孩子RCHILD(i)是结点2i+1(例如i=1,2,3,4,5)

    第20页:二叉树的存储结构

    (4)二叉树的存储结构

    • 顺序存储结构
    • 链式存储结构

    第21页:二叉树的顺序存储结构

    760000543211)顺序存储结构234567

    • 用一组连续的存储单元依次自上而下、自左至有右存储完全二叉树上的结点元素
    • 数据结构定义:
    • #defineMAX_TREE_SIZE100//二叉树的最大结点数
    • typedefTelemTypeSqBiTree[MAX_TREE_SIZE];//0号单元存储根结点
    • SqBiTreebt;1

    第22页:顺序存储结构的特点

    特点:

    • 深度为k,需要开辟2k-1个结点的存储空间
    • 结点间关系蕴含在其存储位置中
    • 浪费空间,适于存满二叉树和完全二叉树
    • 单支树不合算1234000000040003021

    第23页:二叉树的链式存储结构

    2)链式存储结构

    • 利用结点和指针建立链式结构
    • 二叉树链表结点包含三个或四个域
    • 数据域、左、右孩子指针、双亲指针
    • 存储结构分
    • 二叉链表、三叉链表
    • 链表的头指向二叉树的根结点
    • n个结点的二叉链表有n+1个空域,利用它可以构成线索链表ParentLChildRChildDatarchilddataLchildrchildparentdataLchild

    第24页:二叉链表存储表示

    二叉链表存储表示

    • typedefstructBiTNode{TelemTypedata;StructBiTNode*lchild,*rchild;//左右孩子指针
    • }BiTNode,*Bitree;

    第25页:二叉树的基本函数

    基本函数

    • StatusCreateBitree(Bitree&T);//构造二叉链表表示的二叉树T
    • StatusPreorderTraverse(BiTreeT,status(*Visit)(Telemtypee));//先序遍历二叉树T,对每个结点调用函数visit一次且仅一次
    • StatusInorderTraverse(BiTreeT,status(*Visit)(Telemtypee));//中序遍历
    • StatusPostorderTraverse(BiTreeT,status(*Visit)(Telemtypee));//后序遍历
    • StatusLevelorderTraverse(BiTreeT,status(*Visit)(Telemtypee));//层序遍历

    第26页:二叉链表存储结构示例

    rchilddatalchildABCDEFG在n个结点的二叉链表中,有n+1个空指针域ABDE^^C^^^F^^G^

    • 二叉链表存储结构

    第27页:第三节遍历二叉树和线索二叉树

    第三节遍历二叉树和线索二叉树

    第28页:遍历二叉树

    (1)遍历二叉树

    • 遍历含义:按照某种搜索路径访问树中的每一个结点,使得每个结点均访问一次,且仅访问一次
    • 遍历方案:
    • DLR、LDR、LRD、DRL、RDL、RLD
    • 限定从左到右方案:
    • 先序(根)遍历
    • 中序(根)遍历
    • 后序(根)遍历

    第29页:先序遍历递归算法

    1)基于递规算法的先序遍历(算法6.1)

    • //先序遍历二叉树T的递归算法,对每个数据元素调用函数visit,最简单的visit函数是打印元素
    • StatusPreorderTraverse(BiTreeT,status(*Visit)(Telemtypee)){if(T){if(visit(T->data))if(PreorderTraverse(T->lchild,visit))if(PreorderTraverse(T->rchild,visit))returnOK;returnERROR;}elsereturnOK;
    • }//PreorderTraverse

    第30页:先序遍历递归过程示意

    先序遍历主程序Pre(T)返回返回pre(TR);返回返回pre(TR);TBprintf(B);pre(TL);BTAprintf(A);ATDprintf(D);pre(TL);TCprintf(C);pre(TL);返回T>左是空返回pre(TR);T>T>T>T>C左是空返回右是空返回D左是空返回右是空返回pre(TL);pre(TR);先序序列:ABDCvoidpreorder(JD*bt){if(bt!=NULL){printf("%d\t",bt->data);preorder(bt->lchild);preorder(bt->rchild);}}

    第31页:中序遍历递归算法

    //中序遍历

    • StatusInorderTraverse(BiTreeT,status(*Visit)(Telemtypee)){if(T){if(InorderTraverse(T->lchild,visit))if(visit(T->data))if(InorderTraverse(T->rchild,visit))returnERROR;}elsereturnOK;
    • }//InorderTraversereturnOK;

    第32页:三种遍历举例

    2)三种遍历举例

    • 先序:
    • -*a bc
    • 中序:
    • a*b–c
    • 后序:
    • ab*c-

    第33页:先序遍历序列示例

    ADBCDLRADLRDLR>B>>D>>CDLR先序遍历序列:ABDC

    • 简单的先序遍历

    第34页:中序遍历序列示例

    ADBCLDRBLDRLDR>A>>>>CLDRD中序遍历序列:BDAC

    • 简单的中序遍历

    第35页:后序遍历序列示例

    ADBCLRDLRD>A>>>>CLRDLRDD后序遍历序列:DBCA

    • 简单的后序遍历B

    第36页:遍历二叉树的应用程序

    递规算法的遍历二叉树应用程序二叉树:char*s="A(B(D,E(H,I)),C(G))";

    第37页:非递归遍历算法的思路

    3)基于非递规算法的遍历算法

    • 递规算法简单精炼,但效率低
    • 非递规算法基本思路:设立一个堆栈S,用来记录结点的指针,以便遍历二叉树

    第38页:中序遍历非递归算法

    //中序遍历二叉树T的非递归算法,对每个数据元素调用函数visit

    • StatusInorderTraverse(BiTreeT,status(*Visit)(Telemtypee)){InitStack(S);Push(S,T);//根指针进栈while(!StackEmpty(S)){while(GetTop(S,p)&&p)Push(S,p->lchild);//向左走到尽头//p不能为空,但p->lchild可以为空Pop(S,p);//空指针退栈,因为最后一个叶结点没有左孩子if(!StackEmpty(S)) {//访问结点,向右退一步Pop(S,p);if(!Visit(p->data))returnERROR;Push(S,p->rchlid);}//if}//whilereturnOK;
    • }//特点:左树到尽头,最后空指针进栈ABCDEFGHI

    第39页:中序遍历非递归算法续

    //中序遍历二叉树T的非递归算法,对每个数据元素调用函数visit

    • StatusInorderTraverse(BiTreeT,status(*Visit)(Telemtypee)){InitStack(S);p=T;while(p‖!StackEmpty(S)) {//保证空指针不进栈if(p){Push(S,p);p=p->lchild; }//指针进栈,遍历左子树//p不能为空,栈中无空数据元素else{//根指针退栈,访问根结点,遍历右子树Pop(S,p);if(!Visit(p->data))returnERROR;p=p->rchild;}//else}//whilereturnOk;
    • }

      第40页:中序遍历非递归演示

      ABCDEFGpiP->A(1)ABCDEFGpiP->BP->A(2)ABCDEFGpiP->CP->BP->A(3)ABCp=NULLDEFGiP->BP->A访问:C(4)

      • 中序遍历二叉树(空指针不进栈):演示如下

      第41页:中序遍历演示续

      pABCDEFGiP->A访问:CB(5)ABCDEFGiP->DP->A访问:CBp(6)ABCDEFGiP->EP->DP->A访问:CBp(7)ABCDEFGiP->DP->A访问:CBEp(8)演示如下(续)

      第42页:中序遍历演示续二

      ABCDEFGiP->GP->DP->A访问:CBEP=NULL(9)ABCDEFGiP->A访问:CBEGDp(11)ABCDEFGiP->FP->A访问:CBEGDp(12)ABCDEFGiP->DP->A访问:CBEGp(10)演示如下(续)

      第43页:中序遍历演示续三

      ABCDEGiFp=NULLP->A访问:CBEGDF(13)ABCDEFi访问:CBEGDFApG(14)CDEFGp=NULLABi访问:CBEGDFA(15)演示如下(续)

      第44页:构造二叉树算法

      4)构造二叉树算法//构造二叉链表表示的二叉树T StatusCreateBitree(Bitree&T){ scanf(&ch); if(ch==’‘)T=NULL; else{ if(!(T=(BiTNode*)malloc(sizeof(BiTNode))))exit(overflow); T->data=ch;CreateBiTree(T->rchild);returnOk; } 

      第45页:遍历算法的时间与空间复杂度

      遍历二叉树算法的基本操作是访问结点,无任那种遍历,对含n个结点的二叉树,其时间复杂度为O(n)

      • 遍历过程中栈的最大容量为树的深度,最坏情况(单支)为n,所以空间复杂度为S(n)

      第46页:线索二叉树的问题引出

      (2)线索二叉树

      • 问题引出:
      • 遍历二叉树实质是非线性结构的线性化,且每个结点有且仅有一个直接前驱和直接后继,但二叉链表中没有保留
      • 为此可在每个结点上增加两个指针,分别志向他的直接前驱和后继
      • n个结点的二叉链表中必定存在n+1个空链域,可以利用这些空域来存放结点的前驱和后继信息
      • 结点设计
      • 除左、右子树指针外,设置两个标志域:ltag,rtag
      • lchild,指向左孩子(ltag=0)或前驱(ltag=1)
      • rchild,指向右孩子(rtag=0)或后继(rltag=1)rchildrtagdataltagLchild

      第47页:线索与线索化概念

      线索链表:当由包含左右标志域和左右树指针域的结点的数据结构构成的二叉链表作为二叉树的存储结构时,称为线索链表

      • 线索:指向结点前驱和后继的指针(lchild,rchild)
      • 线索二叉树:加上线素的二叉树
      • 线索化:对二叉树以某种次序遍历使其变为线索二叉树的过程1)概念rchildrtagdataltagLchild

      第48页:线索二叉树举例

      2)例:表达式a+b*(c-d)-e/f的线索二叉树中序遍历情况:a+b*c-d-e/f

      • 叶结点的左链是线索直接指向前驱,叶结点的右链是线索直接指向后继
      • 中间结点无法直接得到前驱和后继,后继结点为右子树的最左下的结点,例如*的后继结点为c;前驱为左子树中最后访问的结点(东华左理子工学树院中程朋最根右下的结点),例如-(根)的前驱结点为d头结点

      第49页:线索链表结点结构

      Typedefenum{Link,Thread}PointerTag;//Link=0:指针,Thread==1:线索

      • typedefstructBiThrNode{TelemTypedata;StructBiThrNode*lchild,*rchild;//左右孩子指针PointerTagLtag,Rtag;
      • }BiThrNode,*BiThrTree;
      • 二叉树的线索链表增加头结点,并约定:
      • lchild指向树的根结点,rchild指向中序遍历时访问的最后一个结点
      • 同时中序系列中的第一个结点的lchild和最后一个结点的rchild指向头结点
      • 建立双向线索链表,可以从第一个结点起顺后续进行遍历,也可以从最后一个结点起顺前驱进行遍历3)二叉树线索存储结构

      第50页:线索链表的遍历算法

      4)基于双向线索链表的二叉树遍历算法

      • //T指向头结点,头结点的左链lchild指向根结点
      • //中序遍历二叉树T的非递归算法,对每个数据元素调用函数visit
      • StatusInorderTraverse_Thr(BiThrTreeT, Status(*visit)(Telemtypee)){p=T->lchild;while(p!=T){//p指向根结点//空树或遍历结束时,p==Twhile(p->Ltag==Link)p=p->lchild;//Link=0,向左到尽头if(!Visit(p->data))returnERROR;//访问其左子树为空的结点while(p->Rtag==Thread&&p->rchild!=T){p=p->rchild;Visit(p->data);}p=p->rchild;//访问后继结点}returnOk;
      • }//InorderTraverse_Thr第一次-,+,a+*

      第51页:中序线索化链表算法

      5)中序遍历建立中序线索化链表算法//线索化的过程即为在遍历的过程中修改空指针的过程 Pre始终指向刚刚访问过的结点,若p为当前访问的结点,则pre为p的前驱 StatusInorderThreading(BiThrtree&Thrt,BiThrTreeT){ if(!(Thrt=(BiThrTree)malloc(sizeof(BiThrNode))))exit(OVERFLOW); Thrt->Ltag=Link; Thrt->Rtag=Thread;if(!T)Thrt->lchild=Thrt; else{ Thrt->lchild=T//根结点; pre=Thrt;//中序遍历进行中序线索化pre->rchild=Thrt; pre->Rtag=Thread;} returnOk; } 

      第52页:中序线索化的递归实现

      //中序遍历进行中序线索化(递归方式)

      • voidInThreading(BiTrSTreep){if(p){InThreading(p->1child);//左子树线索化if(!p->1child){p->Ltag=Thread;p->1child=pre;}//当前结点的左子树为空,则建立指向其前驱结点的前驱线索if(!pre->rchild){pre->Rtag=Thread;pre->rchild=p;}//若前驱结点不为空,且其右子树为空,则建立前驱结点指向当前结点的后继线索pre=p;InThreading(p->rchild);//保持pre指向p的驱//右子树线索化}
      • }//InTheading6)中序遍历进行中序线索化

      第53页:第四节树和森林

      第四节树和森林

      第54页:树和森林的内容安排

      (1)树的存储结构

      • (2)森林与二叉树的转换
      • (3)树和森林的遍历

      第55页:树的存储结构

      (1)树的存储结构

      • 1)双亲表示法
      • 2)孩子表示法
      • 3)孩子兄弟表示法返回

      第56页:双亲表示法

      1)双亲表示法

      • 以一组连续空间存储树的结点、每个结点附设一个指针指向其双亲
      • #defineMAX_TREE_SIZE100
      • typedefstructPTNode{TelemTypedata;intparent;
      • }PTNode;
      • typedefstruct{//双亲位置域PTNodenodes[MAX_TREE_SIZE];intn;
      • }Ptree;
      • 特点://结点数
      • 求父结点方便、可以在常量时间内实现
      • 求结点的孩子时需要遍历整个结构-1R00A10B20C31D41E53F66G76H86K9RBCDEFGHKA返回

      第57页:孩子表示法

      2)孩子表示法

      • 每个结点有多棵子树、存在多个指针域。保存方法:
      • 各结点的结构相同,孩子指针数为树的度,浪费空间
      • 各结点的结构不同,节省空间、操作困难
      • 每个结点的孩子排列起来,构成n个孩子线性表;每个结点排列起来构成一个线性表,同时采用顺序存储结构
      • typedefstructCTNode{//孩子结点intchild;structCTNode*next;
      • }*ChildPtr;
      • typedefstruct{intdata;ChildPtrfirstchild;
      • }CTBox;
      • typedefstruct{CTBoxnodes[MAX_TREE_SIZE];intn,r;//结点数和根的位置;
      • }Ctree;//特点:适合对孩子的操作,但不适合对双亲的操作,可设计
      • 带双亲的孩子链表K9H8G7F6E5R4D3C2B10A301789256返回

      第58页:带双亲的孩子链表

      KHGFERDCBA6968672605-1403424140301789256带双亲的孩子链表返回

      第59页:孩子兄弟表示法

      3)孩子兄弟表示法

      • 又称二叉树表示法,或二叉链表表示法,链表中结点的两个链域分别指向该结点的第一个孩子和下一个兄弟结点
      • typedefstructCSNode{ElemTypedata;StructSCNode*firstchild,*nextsibling;
      • }CSNode,*CSTree;RBCDEFGHKARABCGEFDHK返回

      第60页:森林与二叉树的转换

      (2)森林与二叉树的转换

      • 二叉树和树都可以用二叉链表做存储结构,因而可以用二叉链表作为二叉树和树之间转换的桥梁

      第61页:树与二叉树的对应关系

      树与二叉树转换BCED树ABCDE二叉树AA^^BC^D^^E^A^^BC^D^^E^A^^BC^D^^E^对应存储存储解释解释

      第62页:树转换成二叉树的方法

      树(T)�二叉树(B)的转换方法与步骤

      • 连兄弟
      • 留第一个孩子、断其它孩子
      • 以根为轴心将整个树顺时针转45度

      第63页:树转换成二叉树的步骤

      加线:在兄弟之间加一连线

      • 抹线:对每个结点,除了其左孩子外,去除其与其余孩子之B间的关系
      • 旋转:以树的根结点为轴心,将整树顺时针转45°AACDBCDEFGHIEFGHIABCDEFGHIABCDEFGHIABCDEFGHI树转换成的二叉树其右子树一定为空1)树转换成二叉树具体步骤

      第64页:二叉树转换成树的步骤

      加线:若p结点是双亲结点的左孩子,则将p的右孩子,右孩子的右孩子,……沿分支找到的所有右孩子,都与p的双亲用线连起来

      • 抹线:抹掉原二叉树中双亲与右孩子之间的连线
      • 调整:将结点按层次排列,形成树结构BCDEFGHIAABCDEFGHIABCDEFGHIABCDEFGHIABCDEFGHI2)二叉树转换成树具体步骤

      第65页:森林与二叉树的转换规则

      森林(F(T1,T2,…,Tm))�二叉树(B)的转换

      • 若F为空,B为空
      • 若F非空,B的根为第一棵树T1的根,B的左子树为T1中根的子树森林转换的二叉树,B的右子树为森林F’(T2,…,Tm)转换成的二叉树

      第66页:森林转换成二叉树的步骤

      生成二叉树:将各棵树分别转换成二叉树

      • 连根结点:将每棵树的根结点用线相连
      • 旋转:以第一棵树根结点为二叉树的根,再以根结点为轴心,顺时针旋转,构成二叉树型结构ABCDFEGHIJBCDEFAGHIJABCDEFGHIJABCDEFGHIJ3)森林转换成二叉树具体步骤

      第67页:二叉树转换成森林的规则

      二叉树(B)-+森林(F(T1,T2,…,Tm))的转换

      • 若B为空,F为空
      • 若B非空,F中第一棵树T1的根为B的根,T1中根结点的子树森林为B的左子树转换而成,其余森林F’(T2,…,Tm)由B的右子树转换成

      第68页:二叉树转换成森林的步骤

      抹线:将二叉树中根结点与其右孩子连线,及沿右分支搜索到的所有右孩子间连线全部抹掉,使之变成孤立的二叉树

      • 还原:将孤立的二叉树还原成树ABCDEFGHIJABCDEFGHIJABCDEFGHIJABCDEFGHIJ4)二叉树转换成森林具体步骤返回

      第69页:树和森林的遍历

      (3)树和森林的遍历

      • 树的遍历方法
      • 先根(次序)遍历树:先访问树的根结点,然后依次先根遍历根的每一棵子树(ABCDE)
      • 后根(次序)遍历树:先依次后根遍历每棵子,然后访问根结点(BDCEA)
      • 森林的遍历方法
      • 先序遍历森林:(ABCDEFGHIJ)
      • 中序遍历森林(BCDAFEHJIG)
      • 森林的先序和中序遍历即为其对应的二叉树的先序和中序遍历ABCEDBCDAEFGHIJ

      第70页:树的遍历示例

      ABCDEFGHIJKLMNO先序遍历:ABEFIGCDHJKLNOM后序遍历:EIFGBCJKNOLMHDA层次遍历:ABCDEFGHIJKLMNO下一节

      第71页:第六节赫夫曼树及其应用

      第六节赫夫曼树及其应用

      第72页:最优二叉树的定义

      哈夫曼树(Huffman)——带权路径长度最短的树

      • 定义
      • 路径:从树中一个结点到另一个结点之间的分支构成这两个结点间的路径
      • 路径长度:路径上的分支数
      • 树的路径长度:从树根到每一个结点的路径长度之和
      • 树的带权路径长度:树中所有带权结点的路径长度之和其中:knkw——权值lk——结点到根的路径长度wlk记作:wplk1(1)最优二叉树(赫夫曼树)

      第73页:赫夫曼树的定义

      Huffman树——设有n个权值{w1,w2,……wn},构造一棵有n个叶子结点的二叉树,每个叶子的权值为wi,则wpl最小的二叉树叫做最优二叉树(赫夫曼树)

      第74页:赫夫曼树构造示例

      例如:有4个结点,权值分别为7,5,2,4,构造有4个叶子结点的二叉树abcd7524WPL=7*2+5*2+2*2+4*2=36dcab2475WPL=7*3+5*3+2*1+4*2=46abcd7524WPL=7*1+5*2+2*3+4*3=35nWPLWKLKk1

      第75页:赫夫曼树的应用背景

      例如,编制一个将百分制转换成五级分制的程序时,先判断哪个分数段后判断哪个分数段?这是就要考虑学生在不同分数段的概率是不同的这个因素,概率大的先判断

      第76页:赫夫曼树的构造步骤

      (2)赫夫曼树构成

      • (1)根据n个权值{w1,w2,….wn}构造n棵二叉树的集合F={T1,T2,…Tm},每棵树只有根结点,无左右子树
      • (2)选两可权值最小的树作为左右子树构造一棵新树,该新树根结点的权为左右子树根结点的权之和
      • (3)在F中删除这两棵树,将新得到的二叉树加入F中
      • (4)中复(2)和(3),直到F中只有一棵树为止

      第77页:赫夫曼树构造过程示例

      例如17a5b2c4d7a5b2c4d67a5b2c4d6117a5b2c4d61118

      第78页:赫夫曼树构造示例二

      例如2w={5, 29, 7, 8, 14, 23, 3,11}52978142331129781423118537815291423118538192914231578111153823192914781553291429291578115381923421153819234214292915785811538192342142929157858100

      第79页:赫夫曼编码的由来

      (2)赫夫曼编码

      • 问题由来
      • 电文传输、出现次数多的字符采取尽可能短的编码可以减少电文长度
      • 设计长短不等的编码,必须是任意一个字符的编码不是另一个字符的前后缀,这种编码称前缀编码
      • 可以利用二叉树来设计二进制的前缀编码,并约定左分支表示字符‘0’,右分支为‘1’,则从根结点到叶结点的路径上分支字符组成的字符串即为该叶结点的字符编码
      • 电文中,可利用n种字符出现的概率做权来设计赫夫曼编码

      第80页:赫夫曼树的存储结构

      1)赫夫曼树和赫夫曼编码的存储结构

      • 由于赫夫曼树中没有度为1的结点,所以一棵具有n个叶结点的赫夫曼树共有2n-1个结点
      • 存储结构
      • typedefstruct{unsignedintweight;usingnedintparent,1child,rchild;
      • }HTNode,*HuffmanTree;//动态分配数组存储赫夫曼树
      • typedefchar**HuffmanCode;//动态分配数组存储赫夫曼编码表abcd

      第81页:算法6.12求赫夫曼编码

      2)求赫夫曼编码算法6.12voidHuffmanCoding(Huffmantree&HT,HuffmanCode&HC,int*w,intn){ if(n<=1)return; m=2*n-1; HT=(Huffmantree)malloc(m+1)*sizeof(HTNode));for(p=HT,i=1; i<=n; ++i,++p,++w)p={ *w,0,0,0} ;for(; i<=m; ++i,++p)*p={ 0,0,0,0} ;for(i=n+1; i<=m; ++i) {HT[s1].parent=i; HT[s2].parent=i; HT[i].1child=s1; HT[i].rchild=s2; HT[i].weight=HT[s1].weight+HT[s2].weghit; } (后续)

      第82页:算法6.12续:逆向求编码

      }//Huffmancoding2)求赫夫曼编码算法6.12(续)

      • //从叶子到根逆向求每个字符的赫夫曼编码
      • HC=(HuffmanCode)malloc((n+1)*sizeof(char*));//分配n个字符编码的头指针向量
      • cd=(char*)malloc(n*sizeof(char*));//分配求编码的工作空间
      • cd[n-1]=‘\0’;
      • for(i=1;i<=n;++i){start=n-1;//编码结束符//逐个字符求赫夫曼编码//编码结束符位置for(c=i;f=HF[i].parent;f!=0//f==0为根//;c=f,f=HT[f].parent){//从叶子到逆向求编码if(HT[f].lchild==c)cd[--start]=‘0’elsecd[--start]=‘1’;}HC[i] =(char*)malloc(n-start)*sizeof(char));//为第i个字符编码分配空间strcpy(HC[i],&cd[start]);//从cd复制编码(串)到HC
      • }
      • free(cd);//释放工作空间startstart=n-1cd

      第83页:算法6.13无栈非递归求编码

      东华理工学

      • 院3)求赫夫曼编码算法6.13HT[i].weight =0;//遍历赫夫曼树时作结点状态标志
      • //从根到叶子,无栈非递归遍历赫夫曼树,求赫夫曼编码
      • HC=(HuffmanCode)malloc((n+1)*sizeof(char*));
      • p=m;cdlen=0;//
      • for(i=1;i<=m;++i)
      • while(p){if(HT[p].weight ==0) {//向左HT[p].weight=1;if(HT[p].lchild!=0){p=HT[p].lchild;cd[cdlen++] =“0” ;}elseif(HT[p].rchild==0){//登记叶子结点的字符编码HC[p]=(char*)malloc((cdlen+1)*sizeof(char));cd[cdlen]=”\0”;strcpy(HC[p],cd);//复制编码(串)}}elseif(HT[p].weight==1){HT[p].weight=2;if(HT[p].rchild!=0){p=HT[p].rchild;cd[cdlen++]=“1”;}}else{//HT[p].weight==2,退回HT[p].weight=0;p=HT[p].parent;--cdlen;//退到父结点,编码长度减}//else程}朋//w根hile//向右

      第84页:本章重点

      本章重点

      • 树的概念、术语
      • 二叉树的性质、存储结构
      • 二叉树遍历、线索二叉树的概念
      • 树的结构、森林与二叉树的转换
      • 赫夫曼树的构造过程和赫夫曼编码

      第85页:习题(一)

      习题:

      • 1、树最适合用来表示:元素之间具有分支层次关系的数据。
      • 2、深度为5的二叉树最多有31个结点。
      • 3、深度为k的完全二叉树至少有2k-1个结点,至多有2k-1个结点,若自上而下,从左到右次序给结点编号(从1开始),则编号最小的叶结点的编号是2k-1。
      • 4、任何一棵二叉树的叶结点在先序、中序、后续遍历中的相对次序不发生变化。
      • 5、已知一棵二叉树的中序遍历序列为cbedahgijf,后序遍历序列为cedbjigfa,画出该二叉树。

      第86页:习题(二)

      6、假设二叉树采用顺序存储结构1画出该二叉树2写出先序遍历、中序遍历和后序遍历的结果3写出结点c的双亲结点、其左、右孩子4画出把此二叉树还原成森林的图b0000ih00jc00g0d0fae作业:

      第87页:习题(三)

      7、有一份电文中共使用了5个字符:a、b、c、d、e,它们出现的频率依次为4、7、5、2、9,试画出对应的赫夫曼树,并求出每个字符的赫夫曼编码。赫夫曼树赫夫曼编码a:001b:10c:01d:000e:11627111624东华理dd工学院程朋aa根7b0115c19e1000

      第88页:二叉树遍历练习图

      acbdefghij

      第89页:练习用二叉树图

      ebadcfihjg二叉树

      第90页:还原成的森林图

      eadfijc还原成的森林bhg