第6章 树和二叉树
Download
1 / 129

第6章 树和二叉树 - PowerPoint PPT Presentation


  • 124 Views
  • Uploaded on

第6章 树和二叉树. 6.1 树的定义和基本术语 6.2 二叉树 6.3 二叉树的遍历 习题1 6.4 线索二叉树 6.5 树和森林 习题2 6.6 哈夫曼树及应用. 6.1 树的定义和基本术语. J. I. A. C. B. D. H. G. F. E. 一、树的概念 非线性数据结构。 树的递归定义: 树( tree) 是 n(n>=0) 个结点的有限集,当 n>0 时, (1)有且仅有一个特定的称为 根( root ) 的结点;

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about ' 第6章 树和二叉树' - aidan-sweet


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
第6章 树和二叉树

6.1 树的定义和基本术语

6.2 二叉树

6.3 二叉树的遍历习题1

6.4 线索二叉树

6.5 树和森林习题2

6.6 哈夫曼树及应用


6.1 树的定义和基本术语

J

I

A

C

B

D

H

G

F

E

一、树的概念

  • 非线性数据结构。

  • 树的递归定义:

    • 树(tree)是n(n>=0)个结点的有限集,当n>0时,

      • (1)有且仅有一个特定的称为根(root)的结点;

      • (2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1,T2,...,Tm,其中每个集合本身又是一棵树。称为子树(subtree)。


J

I

A

C

B

D

H

G

F

E

  • 例:下面的图是一棵树

    T={A, B, C, D, E, F, G, H, I, J}

    • A是根,其余结点可以划分为3个互不相交的集合:

      • T1={B, E, F} , T2={C, G} , T3={D, H, I, J}

    • 这些集合中的每一集合都本身又是一棵树,它们是A的子树。

      • 例如,对于 T1,B是根,其余结点可以划分为2个互不相交的集合:

      • T11={E},T12={F},T11,T12 是B的子树。


J

I

A

C

B

D

H

G

F

E

  • 树的逻辑结构特征:

    • 1)树中只有根结点没有前趋;其余结点都有且仅一个前趋;

    • 3)每个结点,可以有零个或多个后继;

    • 4)除根外的其他结点,都存在唯一条从根到该结点的路径;

    • 5)树是一种分支结构。



二、树的应用

  • 1)树可表示具有分支结构关系的对象

    • 例1.家族族谱

荣国公

贾 赦 贾政

贾珠 贾元春 贾宝玉 贾探春 贾环

贾琏 贾迎春

贾兰

巧姐



C:

文件夹1 文件夹n 文件1 文件2

文件夹11 文件夹12 文件11 文件12


  • 3)一些应用问题的求解过程可用树结构的来描述,以帮助程序员写出求解问题的程序或算法。

    • 例4. 求集合的幂集——求集合A={ 1,2,3} 的幂集

      • 集合中每个元素对于子集来说,要么属于该子集,要么不属于该子集。

考虑元素 1

{ }

考虑元素 2

{1} { }

考虑元素 3

{1,2} {1} {2} { }

{1,2,3} {1,2} {1,3} {1} {2,3} {2} {3} { }

显然集合A中所有即为该树的所有叶子,求集合A所有子集即为求该树的所有叶子。


三、树的表示

  • 1)图示表示

  • 2)二元组表示

  • 3)嵌套集合表示(图a)

  • 4)广义表表示(图b)

  • 5)凹入表示法(类似书的目录,图c)


J

I

A

C

B

D

H

G

F

E

L

K

M

  • 广义表表示法

( A ( B ( E ( K, L ), F ), C ( G ), D ( H ( M ), I, J ) )

根作为由子树森林组成的表的名字写在表的左边


6.1 树的有关概念

J

I

A

C

B

D

H

G

F

E

四、树的 基本术语

  • 树的结点:包含一个数据元素及若干指向子树的分支;

  • 孩子(Child)结点:结点的子树的根;

  • 双亲 (Parent)结点:B结点是A结点的孩子,则A结点是B结点的双亲

  • 兄弟(Sibling)结点:同一双亲的孩子结点;

  • 堂兄结点:同一层上结点;


J

I

A

C

B

D

H

G

F

E

  • 结点的层次(Level):根结点的层定义为1;根的孩子为第二层结点,依此类推;

  • 树的深度(高度,Depth):树中最大的结点层次;

  • 结点的度(Degree):结点子树的个数;

  • 树的度: 树中最大的结点度;

  • 叶子结点(Leaf):也叫终端结点,是度为0的结点;

  • 分支结点:度不为0的结点;


J

I

A

C

B

D

H

G

F

E

  • 有序树:子树从左至右有序的树;

  • 无序树:不考虑子树的顺序;

  • 森林:m (m≥0)棵互不相交的树集合。

    树:Tree=(root, F), 其中

    • 根:root

    • 森林:F={T1, T2, ……,Tm},

    • 子树:Ti={ri, Fi}

    • 当m>0时,树根与子树森林的关系:

      RF={<root, ri>| i=1,2,…m, m>0}


五、树的基本操作

  • 树的应用很广,应用不同基本操作也不同。下面列举了树的一些基本操作:

  • 1)初始化:InitTree(&T);

  • 2)销毁:DestroyTree(&T);

  • 3)构造:CreateTree(&T, definition);

  • 4)清空:ClearTree(&T);

  • 5)判空:TreeEmpty(T);

  • 6)求深度:TreeDepth(T);


  • 7) 求根结点:Root(T);

  • 8)求当前结点的值:Value(T, cur_e);

  • 9) 给当前结点赋值:Assign(T, cur_e, value);

  • 10)求当前结点的双亲:Parent(T, cur_e);

  • 11)求当前结点的左孩子:LeftChild(T, cur_e);

  • 12)求当前结点的右兄弟:RightSibling(T, cur_e);

  • 13)插入子树:InsertChild(&T, &p, i, c);

  • 14)删除子树:DeleteChild(&T,&p, i);

  • 15)遍历:TraverseTree(T, Visit( ));


  • 问题1:树是非线性结构,该怎样存储?

    • 顺序结构,链式结构

  • 问题2:树的顺序存储方案如何设计?

  • 问题3:树的链式存储方案如何设计?

  • 解决思路:先研究最简单、最有规律的树,然后设法把一般的树转化为简单树。


    6.2 二 叉 树树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    • 本章我们主要讨论一种最简单的树——二叉树。

      • 6.2.1 二叉树的定义

      • 6.2.2 二叉树的性质

      • 6.2.3 二叉树的存储结构


    6 2 1
    6.2.1 二叉树的定义树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    E

    D

    C

    A

    B

    G

    F

    一、二叉树的定义

    • 二叉树:度不超过2的有序树。

    • 说明

      • 1)度不超过2

        • 二叉树中每个结点最多有两颗子树

      • 2)有序树

        • 左、右子树次序不能颠例;

      • 3)二叉树的递归定义:

        • 二叉树或为空树,或由根结点及两棵不相交的左子树、右子树构成,且左、右子树本身也是二叉树。


    E树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    D

    E

    D

    C

    C

    B

    A

    A

    B

    G

    G

    F

    F

    (a)

    (b)

    (a)、(b)是不同的二叉树,

    (a)的左子树有四个结点,

    (b)的左子树有两个结点,


    二、二叉树的基本形态树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    φ

    (b)仅有根结点的二叉树;

    (a) 空二叉树

    (e)左子树为空的二叉树

    (d)左、右子树均为非空的二叉树

    (c)右子树为空的二叉树


    三、应用举例树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    c

    e

    a

    d

    b

    f

    +

    /

    -

    -

    *

    • 例1.树能用二叉树表示

    • 例2. 用二叉树表示表达式(P129)

      • 若表达式为数或简单变量,则二叉树仅有一个根结点;

      • 若表达式=(左操作数)(运算符)(右操作数),则

        • 运算符为根结点

        • 左操作数为左子树

        • 右操作数为右子树

    a+b*(c-d)-e/f


    甲 乙树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    开始

    甲 乙 甲 乙

    甲 乙 甲 乙

    甲 乙 甲 乙 甲 乙 甲 乙

    甲 乙甲 乙 甲 乙甲 乙

    • 例3.双人比赛的所有可能的结局

    开局连赢两局

    或五局三胜


    6 2 2
    6.2.2 二叉树的性质树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    E

    D

    C

    A

    B

    G

    F

    • 性质1

      • 在二叉树的第i 层上最多有2i-1个结点(i≥1)。

    • 性质2

      • 深度为k的二叉树最多有 2k-1 个结点(k≥1)。

    • 性质3

      • 设:

        • 二叉树叶子结点数为n0,

        • 度为2的结点数n2,

      • 则n0 = n2 +1。


    • 两种特殊的二叉树树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

      • 满二叉树

      • 完全二叉树


    1树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    2

    3

    4

    5

    6

    7

    E

    B

    A

    D

    A

    C

    C

    B

    F

    G

    • 满二叉树

      • 满二叉树:深度为k,且含有2k-1个结点二叉树。

      • 对满二叉树的结点编号:

        • 从上到下,每一层从左到右

    K=2的满二叉树

    K=3的满二叉树


    1树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    F

    E

    C

    A

    E

    D

    C

    B

    D

    A

    B

    F

    G

    2

    3

    4

    5

    6

    • 完全二叉树

      • 完全二叉树:树中所含的n个结点和满二叉树中编号从1至n的结点一一对应的二叉树。

    1

    2

    3

    4

    5

    6

    7

    满二叉树

    完全二叉树


    B树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    A

    E

    C

    A

    D

    C

    B

    D

    A

    F

    C

    E

    E

    B

    D

    G

    G

    • 即:一棵二叉树只有最下一层结点数可能未达到最大,且最下层结点都集中在该层的最左端。

    (a)

    (b)

    (c)

    • 、(b)完全二叉树,(c) 不是完全二叉树


    两个关于完全二叉树的性质树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    1

    F

    A

    E

    D

    C

    B

    2

    3

    4

    5

    6

    • 性质4

      • 具有n个结点的完全二叉树的深度为k=[log2n]+1

    • 性质5:

      • 在完全二叉树中编号为i的结点,

        1)若有左孩子,则左孩子结点编号为2i;

        2)若有右孩子,则右孩子结点编号为2i+1;

        3)若有双亲,则双亲结点编号为[i/2]。

        注:其中[ ]表示取下整。


    6 2 3
    6.2.3树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。二叉树的存储结构

    一、二叉树的顺序存储结构

    #define MAX_TREE_SIZE 100

    //二叉树的最大结点数

    typedef TElemType SqBiTree[MAX_TREE_SIZE];

    //0号单元存储根结点

    SqBiTree bt;


    1树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    F

    A

    E

    D

    B

    C

    2

    3

    4

    5

    6

    • 完全二叉树的顺序结构

      • 按编号顺序依次存储完全二叉树的元素。

    0 1 2 3 4 5 6 ……

    A B C D E F


    A树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    A

    E

    E

    D

    B

    D

    C

    B

    C

    G

    G

    F

    F

    0 1 2 3 4 5 6 7 8 9 ……

    A B C D E F G

    • 一般二叉树的顺序结构

      • 补齐二叉树所缺少的那些结点,对二叉树结点编号。

      • 将二叉树原有结点按编号存储到 “相应”位置上

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10


    [1]树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    [2]

    [3]

    [4]

    [5]

    [6]

    [7]

    [8]

    [9]

    .

    [16]

    • 最坏情况:单支树

      • K个元素的二叉树最多需要2k-1个空间

    A

    ^

    B

    ^

    ^

    ^

    C

    ^

    D

    E

    A

    B

    C

    D

    缺点:①浪费空间;

    ②插入、删除不便


    E树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    F

    D

    B

    A

    C

    A

    B∧

    C∧

    ∧E∧

    ∧D

    ∧F∧

    二、二叉树的链式存储结构

    • 1. 二叉链表

      • 每个结点至少包含三个域:数据域、左指针域、右指针域

    T


    lchild树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    data

    rchild

    二叉链表的类型定义

    typedef struct BiTNode {

    TElemType data;

    struct BiTNode *lchild,*rchild;

    }BiTNode,*BiTree;


    lchild树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    data

    parent

    rchild

    • 2.三叉链表

      • 每个结点至少包含四个域:数据域、双亲指针域、左指针域、右指针域

    三叉链表的类型定义

    typedef struct TriTNode {

    TElemType data;

    struct TriTNode *lchild,*rchild,*parent;

    }TriTNode,*TriTree;


    6.3 二叉树的遍历树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    • 遍历

      • 按某种搜索路径访问二叉树的每个结点,而且每个结点仅被访问一次。

    • 访问

      • 含义很广,可以是对结点作各种处理,如:修改结点数据、输出结点数据。

    • 遍历是各种数据结构最基本的操作。

      • 对于线性结构遍历是很容易的事。

      • 二叉树是非线性结构,每个结点可能有两个后继,如何访问二叉树的每个结点?


    D树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    E

    C

    G

    B

    A

    F

    一、二叉树的遍历方法

    • 二叉树由根、左子树、右子树三部分组成

    • 二叉树的遍历可以分解为:

      • 访问根

      • 遍历左子树

      • 遍历右子树


    D树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    E

    C

    G

    B

    A

    F

    • 令:

      L:遍历左子树

      D:访问根结点

      R:遍历右子树

    • 有六种遍历方法:

      DLR,LDR,LRD,

      DRL,RDL,RLD

    • 若约定先左后右,有三种遍历方法:

      DLR、LDR、LRD,分别称为

      先(根)序、中(根)序、后(根)序遍历


    D树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    E

    C

    G

    B

    A

    F

    • 1.先序遍历(DLR)

      若二叉树非空

      (1)访问根结点;

      (2)先序遍历左子树;

      (3)先序遍历右子树。

      例:先序遍历右图所示的二叉树

      (1)访问根结点A

      (2)先序遍历左子树:即按DLR的顺序遍历左子树

      (3)先序遍历右子树:即按DLR的顺序遍历右子树

    先序遍历序列:A,B,D,E,G,C,F


    D树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    E

    C

    G

    B

    A

    F

    • 2.中序遍历(LDR)

      若二叉树非空

      (1)中序遍历左子树

      (2)访问根结点

      (3)中序遍历右子树

      例:中序遍历右图所示的二叉树

      (1)中序遍历左子树:即按LDR的顺序遍历左子树

      (2)访问根结点A

      (3)中序遍历右子树:即按LDR的顺序遍历右子树

    中序遍历序列: D,B,G,E,A,C,F


    D树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    E

    C

    G

    B

    A

    F

    • 3.后序遍历(LRD)

      若二叉树非空

      (1)后序遍历左子树

      (2)后序遍历右子树

      (3)访问根结点

      例:后序遍历右图所示的二叉树

      (1)后序遍历左子树:即按LRD的顺序遍历左子树

      (2)后序遍历右子树:即按LRD的顺序遍历右子树

      (3)访问根结点A

    后序遍历序列: D,G,E,B,F,C,A


    c树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    a

    e

    d

    f

    b

    +

    -

    *

    /

    -

    • 例:下图所示的二叉树表示表达式

      a+b*(c-d)-e/f

      先序、中序、后序遍历此二叉树


    c树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    a

    e

    d

    f

    b

    +

    -

    *

    /

    -

    • 对表达式树进行中序,前序和后序遍历,分别得到表达式的中缀、前缀和后缀式。

      • 中缀式:a + b * c – d –e / f

      • 前缀式:- + a * b – c d / e f

      • 后缀式:a b c d - * + e f /-


    c树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    a

    e

    d

    f

    b

    +

    -

    *

    /

    -

    • 前缀式和后缀式都不必采用括号或优先级。

      • 前缀式(波兰式): - + a * b – c d / e f

        • 从右到左扫描表达式,

        • 遇到操作数进栈,

        • 遇到运算符,则操作数出栈运算,中间结果进栈。

      • 后缀式(逆波兰式):a b c d - * + e f /-

        • 从左到右扫描表达式,

        • 遇到操作数进栈,

        • 遇到运算符,则操作数出栈运算,中间结果进栈。


    二. 遍历的递归算法树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    上面介绍了三种遍历方法,显然是用递归的方式给出的,以先序为例:

    • 先序遍历(DLR)的定义:

      若二叉树非空

      (1)访问根结点;

      (2)先序遍历左子树;

      (3)先序遍历右子树;

    先序遍历的递归定义


    • 上面先序遍历的定义等价于:树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

      • 若二叉树为空,结束 ——基本项(也叫终止项)

      • 若二叉树非空 ——递归项

        • (1)访问根结点;

        • (2)先序遍历左子树

        • (3)先序遍历右子树;


    • 下面给出先序、中序、后序遍历递归算法树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    • 为了增加算法的可读性,这里对书上算法作了简化。

      • 没有考虑访问结点出错的情况

        • 我们假设调用函数visit( )访问结点总是成功的。


    T树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    A

    B∧

    C∧

    ∧E∧

    ∧D

    ∧F∧

    1.先序遍历递归算法 

    void PreOrderTraverse(BiTree T, Status (*Visit)(TElemType e)) { //本算法先序遍历以T为根结点指针的二叉树

    //对每个数据元素调用函数Visit( ) if (T) { //若二叉树为空,结束返回 // 若二叉树不为空,访问根结点,遍历左子树,遍历右子树

    Visit(T->data);

    PreOrderTraverse(T->lchild, Visit);

    PreOrderTraverse(T->rchild, Visit); }

    }//PreOrderTraverse


    • 最简单的树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。Visit函数是:

      Status PrintElement(TElemType e)

      { //输出元素e的值

      printf(e);

      return OK;

      }

    • 调用实例:

      PreOrderTraverse(T, PrintElement)


    2.中序遍历递归算法树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    void InOrderTraverse(BiTree T, Status(*Visit)(TElemType e))

    { //本算法中序遍历以T为根结点指针的二叉树

    //对每个数据元素调用函数Visit( )

    if (T) { //若二叉树为空,结束返回//若二叉树不为空,遍历左子树,访问根结点,遍历右子树InOrderTraverse(T->lchild, Visit);

    Visit(T->data); InOrderTraverse(T->rchild, Visit);

    }

    }// InOrderTraverse

    时间效率: O(n) //每个结点只访问一次

    空间效率: O(n)//栈占用的最大辅助空间


    3.树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。后序遍历递归算法

    void PostOrderTraverse(BiTree T, Status(* Visit)(TElemType e))

    { //本算法中序遍历以T为根结点指针的二叉树//对每个数据元素调用函数Visit( ) if (T) { //若二叉树为空,结束返回//若二叉树不为空,遍历左子树,遍历右子树,访问根结点 PostOrderTraverse(T->lchild, Visit); PostOrderTraverse(T->rchild, Visit);

    Visit(T->data);

    }

    }//PostOrderTraverse


    • 4.树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。中序遍历的非递归算法

      算法步骤:

      • 1. 根结点进栈,沿左指针查找左子树。

      • 2. 若有左子树,返回第1步。

      • 3. 若无左子树,判栈空?

        • 空则结束。

        • 非空,则栈顶结点出栈访问。转向右子树,返回1。


    • 中序遍历的非递归算法树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

      void InOrderTraverse(BiTree T, Status(*Visit)(TElemType e))

      {InitStack(S); Push(S, T); // T是根结点

      while (!StackEmpty(S))

      { while (Gettop(S, p)) && p ) Push(S, p->lchild); //向左走到尽头

      Pop (S, p); // 空指针退栈

      if (!StackEmpty(S)) // 访问结点,向右一步

      { Pop(S, p);

      Visit( p->data);

      Push(S, p->rchild);

      }//if

      }//while

      }//InOrderTraverse


    • 算法改进树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

      void InOrderTraverse(BiTree T, Status(*Visit)(TElemType e))

      {InitStack(S); p=T; // T是根结点

      while ( p || !StackEmpty(S))

      { if (p) { Push(S, p); p=p->lchild } ; //根结点进栈,遍历左子树 else { // 访问根结点,向右一步

      Pop(S, p);

      Visit( p->data);

      p=p->rchild;

      }//else

      }//while

      }//InOrderTraverse


    三、遍历的应用举例树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    • 例1. 求二叉树的叶子结点个数

    • 例2. 已知先序序列建立二叉链表(空格代表空树)

    • 例3. 已知结点的先序序列和中序序列,建立二叉链表。


    • 例1. 编写求二叉树的叶子结点个数的算法树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

      • 输入:二叉树的二叉链表

      • 结果:二叉树的叶子结点个数(n)

      • 两种情况:

        • 若二叉树为空,结束 ——终止项

        • 若二叉树非空 ——递归项

          • 如果根的左、右子树空,则是叶子结点,n=1;

          • 如果根的左、右子树非空,则n=左、右子树的叶子结点个数之和。


    T树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    A

    B∧

    C∧

    ∧E∧

    ∧D

    ∧F∧

    void leaf(BiTree T) {

    //采用二叉链表存储二叉树

    //n为全局变量,用于累加二叉树的叶子结点的个数。

    //本算法在先序遍历二叉树的过程中,统计叶子结点的个数

    //第一 次被调用时,n=0

    if(T) { //若二叉树为空,结束返回 if(T->lchild==null && T->rchild==null) n++;

    leaf(T->lchild);

    leaf(T->rchild);

    }//if

    }//leaf


    • 例2. 为二叉树建立二叉链表树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

      • 输入:二叉树的先序序列

      • 结果:二叉树的二叉链表

      • 基本思想:

        输入(在空子树处添加*的二叉树的)先序序列,按先序遍历的顺序,建立二叉链表的所有结点并完成相应结点的链接。


    E树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    E

    B

    D

    F

    D

    B

    F

    A

    A

    C

    C

    *

    *

    *

    *

    *

    *

    *

    先序序列:A B D F C E

    在空子树处添加*的二叉树的先序序列:

    A B D * F * * * C E * * *


    Status 树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。CreateBiTree(BiTree &T) {

    //输入先序序列,按先序遍历的顺序,建立二叉链表

    scanf (&ch);

    if (ch= = ‘* ’)T=NULL; // 若ch= = ‘*’ 则T=NULL返回

    else { //若ch! = ‘*’

    if (! (T=(BiTNode * )malloc(sizeof(BiTNode))))

    exit(OVERFLOW);

    T->data = ch; // 建立(根)结点

    CreateBiTree(T->lchild); //构造左子树链表

    CreateBiTree(T->rchild); //构造右子树链表

    }

    return OK;

    }//CreateBiTree


    A树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    A

    A

    B

    F

    B

    F

    G

    F

    G

    CBED

    G

    C

    D

    C

    D

    E

    E

    • 例3: 已知结点的先序序列和中序序列,求整棵二叉树。

      • 先序序列:A B C D E F G

      • 中序序列:C B E D A F G


    • 例:先序序列:树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。A B C D E F G

      中序序列:C B D A E G F

    A

    B

    E

    ^

    C

    D

    F

    ^

    ^

    ^

    ^

    ^

    G

    ^

    ^


    void 树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。CrtBT(BiTree& T, char pre[], char in[], int ps, int is, int n )

    { // pre[ps..ps+n-1]为二叉树的先序序列,

    // in[is..is+n-1]为二叉树的中序序列,构造二叉链表

    if (n==0) T=NULL;

    else {

    k=Search(in, pre[ps]); // 在中序序列中查询

    if (k== -1) T=NULL;

    else { …… }

    } //else

    } // CrtBT


    if (!(T= new BiTNode)) exit(OVERFLOW);树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    T->data = pre[ps]; //建立根结点

    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.1

    • 写出6.15给出的二叉树的先序、中序、后序遍历序列。

    • 写一算法,求二叉树的深度。


    6.4 线索二叉树树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    • 遍历是二叉树最基本最常用的操作。

      • 1)对二叉树所有结点做各种处理可在遍历过程中实现;

      • 2)查找二叉树某个结点,可通过遍历实现;

    • 与线性表相比,对二叉树的遍历算法要复杂费时。

    • 如果能将二叉树线性化,就可以减化遍历算法,提高遍历速度。


    • 如何将二叉树线性化?树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

      以中序遍历为例

      • 我们看到通过遍历可以得到二叉树结点的中序序列。

      • 若能将中序序列中每个结点前驱、后继信息保存起来,以后再遍历二叉树时就可以根据所保存的结点前驱、后继信息对二叉树进行遍历。


    G树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    C

    E

    A

    H

    D

    B

    F

    1. 线索二叉树

    • 线索:指向结点前驱、后继的指针。

    • 线索二叉树:加上线索的二叉树。

    孩子指针

    前趋指针

    后继指针

    中序遍历序列:D,B,H,E,A,F,C,G

    中序线索二叉树


    2. 线索链表树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    • 线索二叉树的存储方法:

      • 1)为每个结点增加二个指针域。

        • 缺点:要用较多的内存空间。

      • 2)利用二叉链表( 含n个结点)的n+1个空指针域

        • 以这种结点结构构成的二叉链表称为线索链表


    线索链表的类型说明树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    typedef enum{link,thread}PointerTag; //link=0, thread=1

    typedef struct BiThrNode{

    TElemType data;

    Struct BiThrNode *lchild, *rchild; //左右指针域

    PointerTag LTag, RTag; //左右标志域

    } BiThrNode,*BiThrTree

    lchild LTag data RTag lchild

    • Ltag, Rtag:左右标志域

      • 标志域为0,表示左右指针域存储的是左右孩子的指针

      • 标志域为1,表示左右指针域存储的是线索的指针


    G树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    1

    0

    1

    0

    0

    0

    1

    0

    C

    D

    H

    E

    B

    F

    G

    A

    0

    0

    0

    1

    1

    1

    0

    1

    中序线索二叉树

    C

    E

    D

    H

    A

    B

    F

    中序线索链表

    孩子指针

    前趋指针

    后继指针

    中序遍历序列:D,B,H,E,A,F,C,G


    • 如何在中序线索二叉树中找结点的后继:树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

      • Rtag = 1时,rchild所指的结点即为后继;

      • Rtag = 0时,其后继为遍历其右子树时的第一个结点(右子树中最左下结点)。

    • 如何在中序线索二叉树中找结点的前驱:

      • Ltag = 1时,lchild所指的结点即为前驱;

      • Ltag = 0时,其前驱为遍历其左子树时的最后一个结点(左子树中最右下结点)。


    • 为方便起见树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。,仿照线性链表,为线索链表加上一头结点。

    • 约定:

      • 头结点的lchild域:存放线索链表的根结点指针;

      • 头结点的rchild域: 中序序列最后一个结点的指针;

      • 中序序列第一结点lchild域指向头结点;

      • 中序序列最后一个结点的rchild域指向头结点。


    0树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    0

    1

    1

    0

    1

    0

    1

    0

    E

    H

    F

    A

    G

    B

    C

    D

    1

    0

    1

    1

    1

    0

    0

    1

    1

    T

    头结点

    中序遍历序列:D,B,H,E,A,F,C,G

    1)第一结点:最左下结点;

    2)后继结点:

    右标志域为线索:右孩子结点

    右标志域为指针:右子树的最左下结点


    3.线索二叉树的遍历树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    基本步骤:

    • p指向线索链表的根结点(p=T->lchild);

    • 若线索链表非空或未遍历结束,循环:

      • 顺着p左孩子指针找到最左下结点,访问之;

      • 若p所指结点的右孩子域为线索, p的右孩子结点即为后继结点,访问p所指结点;(顺着后继线索访问二叉树中的结点)

      • 一旦线索“中断”,p所指结点的右孩子域为右孩子指针,p=p->rchild,使 p指向右孩子结点;

    • 遍历完,结束。


    线索链表的遍历算法树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    Void InOrderTraverse_Thr(BiThrTree T, Status (* Visit) (TE1emType e)) {

    // T指向头结点,头结点的左链lchild指向根结点,

    // 中序遍历二叉线索树T算法,对每个数据元素调用函数Visit

    p=T->lchild; //p指向根结点

    while(p!=T) { // 空树或遍历结束

    while(p->Ltag= =Link) p= p->lchild; //找到最左下结点

    Visit(p->data); //访问之

    while (p->Rtag= =Thread && p->rchild!=T)

    {// 若p所指结点的右孩子域为线索,且不是最后一个结点

    p=p->rchild; Visit(p->data); //访问后继结点

    }

    p=p->rchild; //右孩子域为指针,在其右子树中找后继

    }

    return OK;

    }//InOrderTraverse_Thr


    • 中序遍历进行中序线索化树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

      void InThreading(BiThrTree p){

      // 指针 pre为全局变量,指向p的前驱

      if (p){InThreading(p->lchild); //左子树线索化if (!p->lchild){

      p->LTag=Thread; p->lchild=pre;} //前驱线索if (!pre->rchild){

      pre->RTag=Thread; pre->rchild=p;} //后继线索pre=p; //保持pre指向p的前驱InThreading(p->rchild); //右子树线索化}}//InThreading


    Status 树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。InOrderThreading(BiThrTree &Thrt, BiThrTree T)

    { // Thrt指向头结点。if (!(Thrt=(BiThrTree)malloc(sizeof(BiThrNode))))

    exit (OVERFLOW); Thrt->LTag=Link; Thrt->RTag=Thread; //建头结点Thrt->rchild=Thrt; //右指针回指if (!T)Thrt->lchild=Thrt; //若二叉树空,则左指针回指else{ Thrt->lchild=T; pre=Thrt; InThreading(T); //中序遍历进行中序线索化pre->rchild=Thrt;

    pre->RTag=Thread; //最后一个结点线索化Thrt->rchild=pre; } return OK;}//InOrderThreading


    6.5 树和森林树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    6.5.1 树

    6.5.2 森林


    6 5 1
    6.5.1 树树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    一.树的存储结构

    • 双亲表示法:保存每个结点的双亲结点位置;

    • 孩子表示法:保存每个结点的孩子结点位置;

    • 孩子兄弟表示法:用二叉链表保存树


    树的存储结构树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    1.双亲表示法

    • 双亲表示法:通过保存每个结点的双亲结点的位置,表示树中结点之间的结构关系。

    • 用一组连续的内存单元存储树的结点,每个结点包含两个域:

      • 数据域

      • “指针域”:用于指示其双亲结点在数组中的位置

    • 双亲表示法适合实现涉及双亲的操作。


    data parent 树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    0

    1

    2

    3

    4

    5

    6

    7

    8

    A -1

    B 0

    C 0

    D 0

    E 1

    F 1

    G 2

    H 3

    I 3

    F

    G

    D

    B

    H

    A

    I

    E

    C

    9

    0

    双亲表示类型定义

    #define MAX_TREEE_SIZE 100

    typedef struct PTNode{

    TelemType data;

    int parent; //双亲位置域

    }PTNode;

    typedef struct{

    PTNode nodes[MAX_TREE_SIZE];

    int r,n; //根的位置和结点数

    }Ptree;

    T.nodes

    T.r

    T.n

    双亲表示图示


    树的存储结构树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    2.孩子表示法

    • 孩子表示法:通过保存每个结点的孩子结点的位置,表示树中结点之间的结构关系。

    • 与双亲表示法相反,孩子表示法适合实现涉及孩子的操作。

  • 还可以将双亲表示与孩子表示法结合:带双亲的孩子链表。


    • 方法1 树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。 :多重链表(类似二叉链表)

      • 两种方式:定长结点,不定长结点

      • 定长结点

        • 优点:结点结构一致,便于实现树的操作。

        • 缺点:浪费一些内存。

      • 不定长结点

        • 优点:节省内存

        • 缺点:不定长会使一些操作实现复杂一些


    • 方式树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。2:孩子链表

      • 孩子链表:对树的每个结点用线性链表存储它的孩子结点。

    树的孩子链表类型定义

    typedef struct CTNode{ //孩子结点

    int child;

    struct CTNode * next;

    }* ChildPtr;

    typedef struct{

    TElemType data;

    ChildPtr firstchild; //孩子链表头指针

    }CTBox;

    typedef struct{

    CTBox nodes[MAX_TREE_SIZE];

    Int n, r;

    }CTree; //孩子链表


    data firstchild树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    T.nodes

    3 ∧

    7

    6 ∧

    4

    2

    1

    5 ∧

    8 ∧

    G

    I

    A

    C

    E

    D

    H

    F

    B

    T.n

    T.r

    9

    0

    0 1 2 3 4 5 6 7 8 …99

    树的孩子链表图示


    树的存储结构树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    3.孩子兄弟表示法

    • 孩子兄弟表示法:用二叉链表作为树的存储结构

      • firstchild:指向第一个孩子

      • nextsibling:指向下一个兄弟

    孩子兄弟表示法类型定义

    typedef struct CSNode{

    TElemType data;

    struct CSNode *firstchild, *nextsibling;

    }CSNode, *CSTree;


    I树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    F

    G

    E

    D

    B

    C

    A

    E

    H

    F

    I

    A

    B

    D

    H

    G

    C

    A

    I

    D

    G

    H

    C

    B

    E

    F

    (a)树

    (b)二叉树

    由此可将

    树与二叉树

    对应起来

    此二叉链表

    既是树的孩子兄弟表示

    又是二叉树的二叉链表示


    树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    结点X的第一个孩子

    结点X紧邻的右兄弟

    二叉树

    结点X的左孩子

    结点X的右孩子

    二、树和二叉树的转换

    • 二叉树与树都可用二叉链表存储,以二叉链表作中介,可导出树与二叉树之间的转换。

    • 树与二叉树转换方法


    I树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    A

    C

    B

    D

    H

    G

    F

    E

    F

    I

    A

    B

    D

    H

    G

    C

    E

    结点X的第一个孩子

    结点X紧邻的右兄弟

    二叉树

    结点X的左孩子

    结点X的右孩子


    F树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    E

    F

    I

    A

    C

    B

    D

    G

    H

    C

    I

    A

    B

    D

    H

    G

    E

    对应二叉树的先序遍历

    三、树的遍历

    • 1)先根遍历:先访问根,然后依次先根遍历根每一棵子树。

      • 例:先根遍历序列——AB E F C G D H I

    • 2)后根遍历:依次后根遍历根的每一颗子树,然后访问根。

      • 例:后根遍历序列——E F BG C H I D A

    对应二叉树的中序遍历


    I树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    A

    C

    B

    D

    H

    G

    F

    E

    • 先根遍历和后根遍历都是对树按深度方向的遍历

    • 也可按宽度方向(按层次)遍历树。

    • 3)层次遍历:自上而下自左至右访问树中每个结点

      • 例:层次遍历序列———ABCDEFGHI


    6 5 2
    6.5.2 森林 树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    D

    B

    A

    I

    H

    F

    E

    C

    G

    N

    M

    L

    J

    K

    P

    O

    • 森林:树的集合

      • 将森林中树的根看成兄弟,可用树孩子兄弟表示法存储森林;

      • 用树与二叉树的转换方法,进行森林与二叉树转换;

      • 可用树的遍历方法对森林进行遍历。

    包含两棵树的森林


    T树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。1 T2 T3

    E

    G

    A

    A

    F

    H

    I

    B

    C

    D

    B

    C

    J

    E

    D

    3棵树的森林

    G

    F

    T1 T2 T3

    H

    E

    G

    A

    I

    F

    H

    B

    J

    C

    I

    D

    森林的二叉树表示

    J

    各棵树的二叉树表示


    一、森林与二叉树的转换树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    • 设森林 F = ( T1, T2, …, Tn );

      T1 = (root,t11, t12, …, t1m);

    • 二叉树 B =( LB, Node(root), RB );

  • 森林→二叉树的转换规则为:

    • 若 F = Φ,则 B = Φ;

    • 否则,

      • 由 ROOT( T1 ) 对应得到 Node(root);

      • 由 (t11, t12, …, t1m ) 对应得到 LB;

      • 由 (T2, T3,…, Tn ) 对应得到 RB.


    • 设森林 树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。F = ( T1, T2, …, Tn );

      T1 = (root,t11, t12, …, t1m);

    • 二叉树 B =( LB, Node(root), RB );

  • 二叉树→森林的转换规则为:

    • 若 B = Φ, 则 F = Φ;

    • 否则,

      • 由 Node(root) 对应得到 ROOT( T1 );

      • 由LB 对应得到 ( t11, t12, …,t1m);

      • 由RB 对应得到 (T2, T3, …, Tn)


  • 二、森林的遍历树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    • 1)先序遍历:从左到右对森林中的每一棵树进行先根遍历

      若森林不空,则

      • 访问森林中第一棵树的根结点;

      • 先序遍历森林中第一棵树的子树森林;

      • 先序遍历森林中(除第一棵树之外)其余树构成的森林。

    对应二叉树的先序遍历


    • 2)树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。中序遍历:从左到右对森林中的每一棵树进行后根遍历

      若森林不空,则

      • 中序遍历森林中第一棵树的子树森林;

      • 访问森林中第一棵树的根结点;

      • 中序遍历森林中(除第一棵树之外)其余树构成的森林。

    对应二叉树的中序遍历


    A树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    T1 T2 T3

    E

    G

    A

    B

    C

    F

    H

    I

    B

    C

    D

    E

    D

    G

    F

    J

    H

    3棵树的森林

    I

    先序遍历序列:ABCDEFGHIJ

    中序遍历序列:BCDAFEHJIG

    J

    森林的二叉树表示


    习题树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    • 6.20(2)

    • 6.67


    6.7 哈夫曼树及应用树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    • 6.7.1 哈夫曼树

    • 6.7.1 哈夫曼编码


    6 7 1
    6.7.1 哈夫曼树树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    N

    M

    L

    J

    K

    P

    O

    一、哈夫曼树的概念

    • 结点的路径长度:从根到该结点的路径长度;

    • 树的路径长度:树中每个结点的路径长度之和。

    • 路径:从一个祖先结点到子孙结点之间的分支

    • 路径长度:路径上的分支数目称为路径长度;


    • 结点的权树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。:根据应用的需要可以给树的结点赋权值;

    • 结点的带权路径长度:从根到该结点的路径长度与该结点权的乘积;

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

      WPL=  wi  li

    • 哈夫曼树(最优二叉树):假设有n个权值{w1 , w2 , … , wn},构造有n个叶子结点的二叉树,每个叶子结点有一个 wi 作为它的权值。则:

      带权路径长度WPL最小的二叉树称为哈夫曼树。


    2树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    7

    c

    a

    4

    5

    2

    7

    4

    5

    a

    b

    c

    d

    b

    d

    4

    5

    7

    2

    a

    b

    c

    d

    • 例:下面三棵二叉树的四个叶子结点a,b,c,d的权值为7、5、2、4

    (a)WPL=7*2+5*2+2*2+4*2 = 36

    (b)WPL=7*3+5*3+2*1+4*2 = 46

    (c)WPL=7*1+5*2+2*3+4*3 = 35


    <60树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    Y

    Y

    N

    5

    e

    <70

    Y

    N

    15

    d

    <80

    Y

    N

    40

    c

    <90

    N

    Y

    30

    10

    b

    a

    二、应用举例

    • 在解某些判定问题时,利用哈夫曼树获得最佳判定算法。

  • 例:编制一个将百分制转换成五级分制的程序。

    • 最直观的方法是利用if语句来的实现,可用二叉树描述判定过程。


  • <60树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    Y

    Y

    N

    5

    e

    <70

    Y

    N

    15

    d

    <80

    Y

    N

    40

    c

    <90

    分数 0-59 60-69 70-79 80-89 90-100

    比例数 0.05 0.15 0.40 0.30 0.10

    N

    Y

    30

    10

    b

    a

    • 设有10000个百分制分数要转换,设学生成绩在5个等级以上的分布如下:

    • 转换10000个分数所需的总比较次数

      = 10000(0.051+0.152+0.43+

      0.3  4+0.1  4)

      =31500


    • 若将分数分布比例看作二叉树叶子结点权值,树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。(0.05  1+0.15  2 +0.4  3+0.3  4+0.1  4)正是该二叉树的带权路径长度。

    • 可见,要想获得效率较高的转换程序,可构造以分数的分布比例为权值的哈夫曼树。


    三、哈夫曼树的构造算法树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    • 1.根据给定的n个权值 ,构造n棵只有一个根结点的二叉树, n个权值分别是这些二叉树根结点的权。设F是由这n棵二叉树构成的集合;

    • 2.在F中选取两棵根结点权值最小的树作为左、右子树,构造一颗新的二叉树,且置新二叉树根的权值=左、右子树根结点权值之和;

    • 3.从F中删除这两颗树,并将新树加入F;

    • 4.重复 2、3,直到F中只含一颗树为止;


    100树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    40

    30

    15

    10

    30

    40

    40

    30

    15

    15

    10

    10

    15

    30

    10

    30

    40

    10

    40

    15

    60

    15

    30

    60

    15

    30

    15

    30

    15

    5

    5

    5

    5

    5

    例:构造以W=(5,15,40,30,10)为权的哈夫曼树。


    <80树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    Y

    N

    <70

    <90

    N

    N

    Y

    Y

    100

    40

    30

    10

    <60

    b

    a

    c

    15

    30

    40

    10

    Y

    Y

    N

    5

    15

    e

    d

    分数 0-59 60-69 70-79 80-89 90-100

    比例数 0.05 0.15 0.40 0.30 0.10

    30

    60

    15

    5

    • 转换10000个分数所需的总比较次数

      WPL= 0.05  4+0.15  3+0.4  1+0.3  2+0.1  4 =2.05

      总比较次数=2.05*10000*2=20500*2

    WPL=5*3+15*3+40*2+30*2+10*2=220


    6 7 2
    6.7.2 哈夫曼编码树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    • 这里介绍用哈夫曼树求解数据的二进制编码。

      • 在进行数据通讯时,涉及数据编码问题。

      • 数据编码:数据与二进制字符串的转换。

        • 例如:邮局发电报,发送方将原文转换成二进制字符串,接收方将二进制字符串还原成原文。

          • 原文 电文(二进制字符串) 原文

    • 数据编码的分类:

      • 等长

      • 不等长


    • 例. 要传输的原文为树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。ABACCDA

      • 设ABCD的编码为

        A:00,B:01,C:10,D:11

      • 发送方:将ABACCDA 转换成 00010010101100

      • 接收方:将 00010010101100 还原为 ABACCDA

    • 在数据输传时,为省节费用总希望传输的二进制串尽可能短。

      • 可利用二叉树设计不等长编码


    • 不等长编码要解决的两个问题树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

      • 如何使二进制串总长最短?

        • 应用中每个字符的使用频率是不一样的。

        • 可让使用频率高的字符用较短编码,使用频率低的字符用较长编码。

      • 克服编码的二义性

        • 如: A:0,B:00,C:1,D:01

        • 发送方:将ABACCDA 转换成 000011010

        • 接收方:?


    一、哈夫曼编码树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    上述问题可通过构造哈夫曼树解决。

    • 前缀编码:任何一个字符的编码都不是另一个字符的编码的前缀。

    • 哈夫曼编码:最优前缀编码。


    100树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    0

    1

    0

    0

    1

    1

    8

    29

    14

    11

    23

    0

    1

    0

    15

    29

    42

    58

    19

    1

    0

    1

    0

    1

    8

    5

    3

    7

    • 例. 某通讯系统使用8种字符,其使用频率分别为

      构造以字符使用频率作为权值的哈夫曼树。

      • 将权值取为整数w=(5,29,7,8,14,23,3,11),按哈夫曼算法构造的一棵哈夫曼树如下:

    对应字符的编码

    a: 0110

    b: 10

    c: 1110

    d: 1111

    e: 110

    f: 00

    g: 0111

    h: 010


    二、构造哈夫曼树的算法树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    • 输入:n个权值

    • 结果:哈夫曼树


    weight树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。 parent lchild rchild

    • 设用一维数组W存储n个权值,用静态三叉链表HT存储哈夫曼树

    存储哈夫曼树的静态三叉链表类型定义

    typedef struct {

    unsigned int weight;

    unsigned int parent, lchild, rchild;

    }HTNode, *HuffmanTree;

    HTNode类型的结构变量


    0树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    100

    8

    23

    11

    14

    29

    29

    42

    15

    19

    58

    7

    3

    5

    8

    w p lch rch

    5 9 0 0

    29 14 0 0

    7 10 0 0

    8 10 0 0

    14 12 0 0

    23 13 0 0

    3 9 0 0

    11 11 0 0

    8 11 1 7

    15 12 3 4

    19 13 8 9

    29 14 5 10

    42 15 6 11

    58 15 2 12

    100 0 13 14

    w,p,lch,rch 是weight,parent,lchild,rchild

    的缩写

    哈夫曼树

    哈夫曼树对应的静态三叉链表


    哈夫曼算法树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    void HuffmanTree(HuffmanTree &HT, int * w, int n){

    //w 存放n个字符的权值(均>0),构造赫夫曼树HT

    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};

    //用给定的n个权值,

    //构造n棵只有一个根结点的二叉树


    for (; i<m; ++i, ++p) * p={ 0, 0, 0, 0}; 树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    for (i=n+1; i<=m; ++i){

    //按构造哈夫曼树的步骤2、3、4,建哈夫曼树

    Select(HT, i-1, s1, s2);

    //在HT[1..i-1] 选择parent为0且weight最小的两个结点

    //其序号分别为s1和s2。

    HT[s1]. parent =i; HT[s2].parent=i;

    HT[i].lchild=s1; HT[i].rchild=s2;

    HT[i].weight=HT[s1].weight+HT[s2].weight;

    //HT[i]存放新子树的根结点

    }

    ……


    W 5 29 7 8 14 23 3 11
    例:用哈夫曼算法构造以树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。w=(5,29,7,8,14,23,3,11)为权值的哈夫曼树

    0

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    W

    HT

    0

    1

    2

    3

    4

    5

    6

    7

    8

    5

    29

    7

    8

    14

    23

    3

    11

    w p lch rch

    5 9 0 0

    29 14 0 0

    7 10 0 0

    8 10 0 0

    14 12 0 0

    23 13 0 0

    3 9 0 0

    11 11 0 0

    8 11 1 7

    15 12 3 4

    19 13 8 9

    29 14 5 10

    42 15 6 11

    58 15 2 12

    100 0 13 14

    算法原始数据

    算法结果数据

    权值数组W

    哈夫曼树对应的静态三叉链表HT


    w p lch rch树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    0

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    HT

    哈夫曼算法主要步骤图示

    为哈夫曼树

    分配存储空间


    w p lch rch树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    0

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    HT

    • 5 0 0 0

    • 29 0 0 0

    • 7 0 0 0

    • 8 0 0 0

    • 14 0 0 0

    • 23 0 0 0

    • 3 0 0 0

    • 0 0 0

    11

    23

    14

    29

    5

    3

    7

    8

    算法的第一个FOR循环的执行结果

    8棵只有一个结点的二叉树


    0树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    HT

    100

    8

    29

    14

    23

    11

    15

    42

    58

    19

    29

    3

    5

    7

    8

    5 9 0 0

    29 14 0 0

    7 10 0 0

    8 10 0 0

    14 12 0 0

    23 13 0 0

    3 9 0 0

    11 11 0 0

    8 11 1 7

    15 12 3 4

    19 13 8 9

    29 14 5 10

    42 15 6 11

    58 15 2 12

    100 0 13 14

    静态三叉链表HT对应的哈夫曼树

    构造哈夫曼树的过程中,新生成的结点。

    完整的哈夫曼树


    HC树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    100

    0 1 1 0

    1

    2

    3

    4

    5

    6

    7

    8

    1 0

    1 1 1 0

    1 1 1 1

    1 1 0

    8

    11

    14

    29

    23

    0 0

    15

    19

    58

    29

    42

    0 1 1 1

    0 1 0

    8

    7

    3

    5

    //从叶子到根逆向求每个字符的赫夫曼编码

    HC=(HffmanCode)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=HT[i].parent; f!=0; c=f, f=Ht[f].parent)

    if(HT[f].lchild==c)

    cd[--start]="0";

    else cd[--start]="1";

    //为第i个字符编码分配空间

    HC[i]=(char *)malloc((n-start)*sizeof(char));

    strcpy(HC[i], &cd[start]); //从cd复制编码(串)到HC

    }

    free(cd); //释放工作空间

    }//HuffanCoding


    小结树是一种分支结构的对象,在树的概念中,对每一个结点孩子的个数没有限制,因此树的形态多种多样。

    1)二叉树的结构特性、各种存储结构的特点及适用范围

    3)遍历二叉树的递归算法

    4)二叉树线索化

    5)树的存储结构,树和森林与二叉树的转换

    6)哈夫曼树


    ad