写出二叉树的二叉链表和三叉链表的存储结构_数据结构(C语言版)_笔记_5

第6章 树和二叉树

前面讨论了线性结构,下面开始讨论一类重要的非线性数据结构——树型结构(重点是二叉树)。

6.1 树的定义和基本术语

  1. 树(Tree)是n(n≥0)个结点的有限集:
  • n=0时为空树;
  • 在任意一棵非空树中:
  • 有且仅有一个特定的称为根(Root)的结点(n=1时仅有一个根结点);
  • n>1时,其余结点可分为若干个互不相交的有限集,其中每一个集合本身又是一棵树,并且称为根的子树(SubTree)
和广义表的定义类似,树的结构定义也是一个递归的定义,因为在定义中又用到了树的概念,这也是树的固有特性。树的其他表示形式:

写出二叉树的二叉链表和三叉链表的存储结构_数据结构(C语言版)_笔记_5_第1张图片

一般来说,分等级的分类方案都可用树这种层次结构来表示。基本术语:
  • 结点:包含一个数据元素和若干个指向其子树的分支;
  • (结点的)度(Degree):结点拥有的子树的个数;
  • 叶子(Leaf):度为零的结点,也称终端结点;
  • 分支结点:度不为零的结点,也称非终端结点(除根结点之外的分支结点也称内部结点);
  • (树的):树内各结点的度的最大值;
  • 孩子(Child):结点的子树的根;
  • 双亲(Parent):上一条提到的结点(一个结点);
  • 兄弟(Sibling):同一个双亲的孩子之间互称兄弟;
  • 祖先:从根到该结点所经分支上的所有结点;
  • 子孙:以某结点为根的子树中的任一结点都称为该结点的子孙;
  • 层次(Level):从根开始定义,根为第一层,根的孩子为第二层。若某结点在第l层,则其子树的根就在第l+1层;
  • 堂兄弟:双亲在同一层的结点互为堂兄弟;
  • (树的)深度(Depth):树中结点的最大层次,也称高度;
  • 有序树:树中结点的各子树从左至右是有次序的,不能互换。有序树中最左边的子树的根称为第一个孩子,最右边的称为最后一个孩子;
  • 无序树:树中结点的各子树是没有次序的,可以互换;
  • 森林(Forest):m(m≥0)棵互不相交的树的集合。树中每个结点的子树的集合即为森林。由此,也可以用森林和树的相互递归的定义来描述树。

6.2 二叉树

6.2.1 二叉树的定义

  1. 二叉树(Binary Tree)是另一种树型结构:

  • 每个结点至多只有两棵子树(即不存在度大于2的结点);
  • 子树有左右之分,次序不能任意颠倒。

二叉树或为空,或是由一个根结点加上两棵分别称为左子树和右子树的、互不相交的二叉树组成。

即,二叉树可以是空树、可以是只有一个结点的树、可以是根只有左子树的树、可以是根只有右子树的树、可以是一般的二叉树。

完全二叉树和满二叉树是两种特殊形态的二叉树:

  • 一棵深度为且有个结点的二叉树称为满二叉树

    特点是每一层上的结点数都是最大结点数。

  • 从根结点起,从上到下、从左到右对满二叉树的结点进行连续编号。

    深度为k的有n个结点的二叉树,当且仅当其每一个结点都与深度为k的满二叉树中编号从1至n的结点一一对应时,称之为完全二叉树。(满二叉树从右下角向左减少)

    特点是

      • 叶子结点只可能在层次最大的两层上出现;
      • 对任一结点,若其右分支下的子孙的最大层次为l,则其左分支下的子孙的最大层次必为l或l+1。

6.2.2 二叉树的性质

  1. 性质1 在二叉树的第层上至多有个结点

    等比数列通项公式。

  2. 性质2 深度为的二叉树至多有个结点

    等比数列前项和。

  3. 性质3 对任何一棵二叉树,如果其叶子结点数为,度为2的结点数为,则

  • 从下往上看:结点由度为0、度为1和度为2的结点组成,故结点总数;
  • 从上往下看:除根结点外,每个结点或接受了度为1的结点发出的一个分支,或接受了度为2的结点发出的两个分支,故有。

性质4 具有个结点的完全二叉树的深度为

设深度为:

  • 是层的结点总数上限,故有;
  • 是层的结点总数上限,故有。

性质5 如果对一棵有个结点的完全二叉树的结点按层序编号(从上到下、从左到右),则对任一结点,有

  • 如果,则结点是二叉树的根,无双亲;如果,则其双亲PARENT(i)是结点;
  • 如果,则结点无左孩子(结点为叶子结点);否则其左孩子LCHILD(i)是结点;
  • 如果,则结点无右孩子;否则其右孩子RCHILD(i)是结点。

6.2.3 二叉树的存储结构

  1. 顺序存储结构:用一组地址连续的存储单元依次自上而下、自左至右存储完全二叉树上的结点元素。若要存储一般二叉树,则用零占位。

  2. 链式存储结构:结点至少包含3个域:数据域和左、右指针域(二叉链表)。有时为了便于找到结点的双亲,还可增加一个指向其双亲结点的指针域(三叉链表)。

    链表的头指针指向二叉树的根结点。

    在含有n个结点的二叉链表中有2n-(n-1)=n+1个空链域,可以存储其他有用信息,从而得到另一种链式存储结构——线索链表。

6.3 遍历二叉树和线索二叉树

6.3.1 遍历二叉树

  1. 为了在树中查找具有某种特征的结点,或者对树中全部结点逐一进行某种处理,提出了遍历二叉树(traversing binary tree)的问题,即如何按某条搜索路径巡访树中每个结点,使得每个结点均被访问一次,而且仅被访问一次。
  2. 线性结构的遍历很容易,树这种非线性结构的遍历则不然。所以需要将二叉树排列在一个线性队列上,从而便于遍历。
  3. 由于二叉树由根结点、左子树和右子树这三个基本单元组成,因此,若能依次遍历这三部分,便是遍历了整个二叉树。
  4. 二叉树的遍历分为先(根)序遍历中(根)序遍历后(根)序遍历
  5. 遍历二叉树是二叉树各种操作的基础,可以在遍历过程中求结点的双亲、求结点的孩子、判定结点所在层次等。反之,也可在遍历过程中生成结点,建立二叉树的存储结构。
  6. 除了先序、中序和后序遍历,还可从上到下、从左到右按层次进行。
  7. 对n个结点的二叉树,遍历算法的时间复杂度为O(n),空间复杂度也为O(n)。

6.3.2 线索二叉树

  1. 遍历二叉树是对一个非线性结构进行线性化操作的过程,可以得到某个结点的前驱和后继。

  2. 但是当以二叉链表存储时,只能找到结点的左右孩子,而不能直接得到结点在任一序列中的前驱和后继信息。

  3. 这些信息只能在遍历的动态过程中才能得到,为了保存这些信息,可以在结点结构中增加两个指针域fwd和bkwd分别指示前驱和后继信息。

    这样就可以使结构的存储密度大大降低。

  4. 若结点有左子树,则其lchild域指示其左孩子,否则指示其前驱。再增加LTag域,为0说明是第一种情况,为1说明是第二种情况。rchild和RTag同理。

    以这种结点结构构成的二叉链表作为二叉树的存储结构,叫做线索链表

    其中指向结点前驱和后继的指针叫做线索,加上线索的二叉树叫做线索二叉树(Threaded Binary Tree)

    图(a)为中序线索二叉树,图(b)为与其对应的中序线索链表:

    写出二叉树的二叉链表和三叉链表的存储结构_数据结构(C语言版)_笔记_5_第2张图片

  5. 对二叉树以某种次序遍历使其变为线索二叉树的过程叫做线索化

  6. 在线索树上进行遍历,只要先找到序列中的第一个结点,然后依次找结点后继直至其后继为空时而止。

    为找到中间结点的后继,根据中序遍历的特点,可知其后继是子树中根结点的右结点最左下的结点。根据这个思路可以沿着指针直至Tag域为1。前驱同理。

  7. 在后序线索树中找结点后继较为复杂:

  • 若为二叉树的根结点,则其后继为空;
  • 若为右孩子或双亲无右子树的左孩子,则其后继为双亲;
  • 若为左孩子且双亲有右子树,则其后继为双亲的右子树上按后序遍历列出的第一个结点。

在中序线索二叉树上遍历二叉树,虽然时间复杂度还是O(n),但常数因子比上节讨论的算法小,且不需要设栈。

因此,若在某程序中所用二叉树需经常遍历或查找结点在遍历所得线性序列中的前驱和后继,则应采用线索链表作存储结构。

在二叉树的线索链表上添加头结点,令:

令二叉树中序序列中的第一个结点的lchild域的指针和最后一个结点的rchild域的指针均指向头结点。

这好比为二叉树建立了一个双向线索链表,既可以从第一个结点起顺后继进行遍历,也可以从最后一个结点起顺前驱进行遍历。

  • lchild域的指针指向二叉树的根结点;
  • rchild域的指针指向中序遍历时访问的最后一个结点;

二叉树的线索化实质是将二叉链表中的空指针改为指向前驱或后继的元素,而前驱或后继的信息只有在遍历时才能得到,因此线索化的过程即为在遍历的过程中修改空指针的过程。

在遍历过程中,记下访问结点的先后关系,附设一个指针pre始终指向刚刚访问过的结点,若指针p指向当前访问的结点,则pre指向它的前驱。

6.4 树和森林

6.4.1 树的存储结构

  1. 双亲表示法

    以一组连续空间存储树的结点,同时在每个结点中指示其双亲结点在链表中的位置:

    原理:利用了“每个结点(除根以外)只有唯一的双亲”的性质。

    优点:可以快速找到根。

    缺点:求结点的孩子时需要遍历整个结构。

    写出二叉树的二叉链表和三叉链表的存储结构_数据结构(C语言版)_笔记_5_第3张图片

  2. 孩子表示法

    每个结点有多个指针域,每个指针指向一棵子树的根结点:

    优点:便于那些涉及孩子的操作的实现。

    缺点:不适用于涉及双亲的操作。

    写出二叉树的二叉链表和三叉链表的存储结构_数据结构(C语言版)_笔记_5_第4张图片

    原理:

  • 根据树的度来确定要有几个指针域,但要么浪费空间,要么操作不便。
  • 把每个结点的孩子结点排列起来,看成是一个线性表,且以单链表作存储结构。所有结点的链表头指针又组成一个线性表,采用顺序存储结构。

可以将前两种方法结合起来:

写出二叉树的二叉链表和三叉链表的存储结构_数据结构(C语言版)_笔记_5_第5张图片

孩子兄弟表示法(二叉树表示法/二叉链表表示法)

以二叉链表作树的存储结构,链表中结点的两个链域分别指向该结点的第一个孩子结点和下一个兄弟结点,分别命名为firstchild域和nextsibling域:

优点:便于实现各种树的操作,如易于实现找结点孩子等的操作。

写出二叉树的二叉链表和三叉链表的存储结构_数据结构(C语言版)_笔记_5_第6张图片

6.4.2 森林与二叉树的转换

  1. 上面我们看到,树和二叉树都可用二叉链表作为存储结构(是相同的二叉链表的不同解释),所以二叉链表可以作为媒介来导出树与二叉树之间的一个对应关系:

    给定一棵树,可以找到唯一一棵二叉树与之对应。

    写出二叉树的二叉链表和三叉链表的存储结构_数据结构(C语言版)_笔记_5_第7张图片

  2. 从树的二叉链表表示可知,它对应的二叉树的右子树必为空。既然有这个空位,就可以依次将若干棵树(森林)转换成二叉树并通过这个空位相连:

    写出二叉树的二叉链表和三叉链表的存储结构_数据结构(C语言版)_笔记_5_第8张图片

6.4.3 树和森林的遍历

  1. 可以先根遍历树(ABCDE),也可以后根遍历树(BDCEA)。注意子树从左至右按次序遍历。

  2. 根据森林和树相互递归的定义,遍历森林也有两种方法:

  • 先序遍历森林(对应的二叉树的先序遍历)

    第一棵树的根结点->先序遍历第一棵树的根结点的子树森林->先序遍历剩余森林

    ABCDEFGHIJ

  • 中序遍历森林(对应的二叉树的中序遍历)

    中序遍历第一棵树的根结点的子树森林->第一棵树的根结点->中序遍历剩余森林

    BCDAFEHJIG

当以二叉链表作树的存储结构时,树的先根遍历和后根遍历可借用二叉树的先序遍历和中序遍历的算法实现。

6.5 树与等价问题

  1. 自反、对称、传递,则为等价关系。
  2. 划分等价类的算法思想也可用于求网络的最小生成树等图的算法中。

6.6 赫夫曼树及其应用

赫夫曼(Huffman)树,又称最优树,是一类带权路径长度最短的树。

6.6.1 最优二叉树(赫夫曼树)

  1. 路径:从树中一个结点到另一个结点之间的分支构成这两个结点之间的路径。

  2. (结点的)路径长度:路径上的分支数目。

  3. (树的)路径长度:从树根到每一结点的路径长度之和。

    完全二叉树就是这种路径长度最短的二叉树。

  4. (结点的)带权路径长度:从该结点到树根之间的路径长度与结点上权的乘积(叶子结点)。

  5. (树的)带权路径长度:树中所有叶子结点的带权路径长度之和,通常记作

  6. 假设有n个权值,试构造一棵有n个叶子结点的二叉树,带相应的权值,则其中带权路径长度WPL最小的二叉树称做最优二叉树赫夫曼树

  7. 构造赫夫曼树的方法:

    这棵树便是赫夫曼树。

  • 将n个权值构成n棵只有一个带权根结点的二叉树,构成集合F;
  • 在F中选取两棵权值最小的树作为左右子树构造一棵新的二叉树,根结点的权值为二者之和;
  • 在F中删除这两棵树,添加新生成的二叉树;
  • 重复上上步和上步,直到F中只含一棵树为止。

6.6.2 赫夫曼编码

  1. 为了使字符串的编码尽可能短,可以将常用字符的编码变短,不常用字符的编码变长。但是这种长短不等的编码导致无法“断句”,为解决这一问题,必须让任一字符的编码都不是另一个字符编码的前缀。这种编码称做前缀编码
  2. 若约定二叉树左右分支分别表示字符'0''1',那么从根结点到某一叶子结点的路径就唯一确定了一种字符编码。
  3. 设计电文总长最短的二进制前缀编码即为以n种字符出现的频率作权,设计一棵赫夫曼树的问题,由此得到的二进制前缀编码便称为赫夫曼编码
  4. 赫夫曼树中没有度为1的结点,这类树又称严格的(strict,正则的)二叉树。
  5. 一棵有n个叶子结点的赫夫曼树共有2n-1个结点,可以存储在一个大小为2n-1的一维数组中。
  6. 赫夫曼树具体算法请走传送门。

6.7 回溯法与树的遍历

  1. 有一类问题要求一组解、全部解或最优解,而不是根据某种确定的计算法则,是利用试探和回溯(Backtracking)的搜索技术求解。
  2. 回溯法也是设计递归过程的一种重要方法,实质上是一个先序遍历一棵“状态树”的过程。这棵树不是遍历前预先建立的,而是隐含在遍历过程中的。

6.8 树的计数

  1. 两棵二叉树相似,是指二者都为空树或二者均不为空树,且它们的左右子树分别相似。
  2. 两棵二叉树等价,是指二者不仅相似,而且所有对应结点上的数据元素都相同。
  3. 二叉树的计数问题就是讨论具有个结点、互不相似的二叉树的数目。
  4. 通过数学方法的计算(书152~154页),得出结论:含有个结点的不相似的二叉树有棵。
  5. 任意一棵二叉树结点的前序序列和中序序列是唯一的,反过来,给定结点的前序序列和中序序列(或给定中序序列和后序序列)也可以确定一棵二叉树;但给定前序序列和后序序列,无法确定一棵二叉树。
  6. 首先根据前序遍历得知根结点->在中序遍历中划分出左右子树->根据前序遍历得知根结点的左右孩子->左右孩子再次将中序遍历划分为四棵子树->……

你可能感兴趣的:(创建一颗二叉链表的树,5个域,中序线索化,数据结构,清华,pdf,c语言版,数据结构c语言版,数据结构c语言版电子书,数据结构c语言版紫色)