数据结构——树和森林


定义

是由一个集合以及在该集合上定义的一种关系构成的。集合中元素成为树的结点,所定义的关系称为父子关系。当集合为空时,是一棵空树;当集合非空时,此时有且仅有一个特定的称为的结点。
如下图所示,a是一棵空树,b是只有一个根节点的树,c是一棵有10个结点,并且高度为2的树。

在这里插入图片描述

森林是m棵互不相交的树的集合。对树中的每个结点而言,其子树的集合即为森林。删去一棵树的根,就得到一个森林。反之,加上一个结点作为树根,森林就变为一棵树。

树的存储结构

1、双亲表示法:用一个一维数组存储每个结点,数组的下标就是结点的指针位置,每个结点包括一个数据与与指向父亲结点的数组下标的域。
双亲表示法
在这种表示法下,寻找一个结点的父结点只需要O(1)时间。而查询儿子或者兄弟结点的操作可能需要遍历整个数组。
为了节省查询时间,可以规定儿子的数组下标值大于父亲数组下标值,而兄弟结点的数组小标志随着兄弟从左到右递增。如上图所示。


2、孩子链表表示法:用一个线性表来存储树的所有结点信息,称为结点表。每个结点建立一个孩子表,孩子表中只存储孩子结点的地址信息(可以是指针、数组下标或者内存地址)。
孩子链表表示法
上图中的孩子表是用单链表来实现的。由于每个结点的孩子数目不确定,所以一般不用数组来实现孩子表,但是可以用数组实现结点表。
在孩子链表表示法中,通过某个结点找到其孩子比较容易,只需要遍历孩子链表即可。
然而要找到某个结点的父结点,却需要对每个结点的孩子链表进行遍历,比较麻烦,因此可以在孩子链表表示法的基础上结合双亲表示法,在每个结点域中附设一个指向双亲结点的域。如下图所示。

带双亲孩子表示法


3、孩子兄弟表示法:又称二叉树表示法,每个结点除了数据域外,还包含第一个孩子和右邻兄弟。如下图所示,B的第一个孩子是D,B的兄弟结点是C。
孩子兄弟表示法


树、森林与二叉树相互转换

通过树的孩子兄弟表示法可以看到,树和二叉树都可以使用二叉链表作为存储结构,因此从二叉链表可以推导出树与二叉树之间的一个对应关系,从实际存储来看,它们二叉链表相同,只是解释不同而已。
树和二叉树
1、树转化二叉树
(1)顺序连接同一结点的兄弟结点;
(2)保留每个结点到其第一个孩子结点的连接作为该结点的左孩子结点,删除这个结点到其他孩子结点的连接;
(3)以树的根结点为中心,顺时针旋转一定角度,使得结构层次分明。
2、森林是若干树的集合,森林转化为二叉树
(1)将森林中的每一刻树转换为相应的二叉树;
(2)将第一棵二叉树不动,从第二棵二叉树开始,依次把后一棵二叉树的根结点作为前一棵二叉树根结点的右孩子,得到二叉树。
森林转化为二叉树
3、二叉树还原为树或森林
按层次序列对二叉树的每个结点做以下操作:
(1)如果是根结点或者是左孩子结点,则不做任何改动;
(2)如果是右孩子,将其当前父亲的父亲设置为当前结点的父结点,若其当前父亲的父亲为空,则改动后其父亲为空。

树的遍历

由树的定义可以得到两种次序遍历树的方法:
(1)先序遍历:访问树的根结点;从左到右,依次先序遍历根的每棵子树。
(2)后序遍历:从左到右,依次后序遍历根的每棵子树;访问树的根结点。
森林的遍历
(1)先序遍历:访问森林第一棵树的根结点;先序遍历第一棵树中根结点的子树树林;先序遍历除去第一棵树后剩余的树构成的森林。上图中森林的先序遍历:ADBEFGCHI
(2)中序遍历:中序遍历第一棵树中根结点的子树树林;访问森林中第一棵树的根结点;中序遍历除去第一棵树后剩余的树构成的森林。上图中森林的中序遍历:DAEFGBHIC
(3)后序遍历:中序遍历第一棵树中根结点的子树树林;中序遍历除去第一棵树后剩余的树构成的森林;访问森林中第一棵树的根结点。上图中森林的后序遍历:DGEFIHCBA

由于树可以看作只有一棵树的森林,所以树的先序遍历和后序遍历分别与森林的先序遍历和中序遍历对应。

而森林的先序。中序、后序遍历与其相对应的二叉树的先序、中序、后序遍历结果相同,因此树的先序和后序遍历可以借助相应二叉树的先序遍历和中序遍历的算法实现。

由先序和中序遍历序列可以唯一确定一棵二叉树;由中序和后序遍历序列可以唯一确定一棵二叉树,但是由先序和后序不能唯一确定一棵二叉树。

例如:先序遍历为:EBADCFHG,中序序列为:ABCDEFGH,可确定二叉树为:
在这里插入图片描述

Huffman树

定义:给定N个权值作为叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的树为哈夫曼树,也称最优树。

哈夫曼
左边树带权路径长度:(2+3+6+9)2=40
右边树带权路径长度:9 + 6
2 +(2+3)*3 = 36

构造哈夫曼树
输入:给定n个权值
构造n棵只有一个根结点的二叉树,n个权值分别是这些二叉树根结点的权,F是由这n棵二叉树构成的集合
重复执行
选取两个最小的实数结点作为二叉树的左右结点,构造新的二叉树,然后根结点为权值之和;
将这两个结点从集合F中删除,加入新构造的树。

在这里插入图片描述

二叉编码树

符号数据在处理之前需要对符号进行二进制编码,例如英文字符的ASCII编码,它使用固定的8为二进制位表示字符,因此是一种定长编码。

为了缩短数据编码长度,可以以采用不定长编码:给使用频度较高的字符编较短的码,这是数据压缩技术的最基本思想。

那么如何给字符进行不定长编码,而使得数据编码的平均长度最短?使用二叉树对字符集中的字符进行编码,方法如下:
(1)将字符集中的所有字符作为二叉树的叶子结点;
(2)每一个“父亲-左孩子”关系对应一位二进制位0,每一个“父亲-右孩子”关系对应一位二进制1;
(3)从根通往每个叶子结点的路径,就对应于相应字符的二进制编码

在这里插入图片描述
A:0,B:100,C:11,D:101
二叉树编码得到的都是前缀编码,前缀编码使得不定长编码在解码过程中不会产生歧义

Huffman编码

对同一个字符集进行编码的二叉树可以有很多,但是哪个编码才是使得编码长度最小的呢?

由于字符集中各种字符出现的概率不同,字符的出现概率决定了编码方案的选择。
其实这将最佳编码方案抽象成为求带权路径长度最小的二叉树,而Huffman树就是这样的树。

参考

数据结构与算法(Java语言版)——周鹏
如有侵权,请联系作者更正

  • 5
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
哈夫曼是一种最优二叉,它的构造算法如下:首先将n个结点作为n棵仅含有一个根结点的二叉,构成一个森林F。然后,生成一个新结点,并从F中找出根结点权值最小的两棵作为它的左右子,新结点的权值为两棵子根结点的权值之和。接着,从F中删除这两个,并将新生成的加入到F中。重复以上步骤,直到F中只有一棵为止。 哈夫曼具有以下性质: 1)每个初始结点都会成为叶节点,双支结点都为新生成的结点 2)权值越大离根结点越近,反之权值越小离根结点越远 3)哈夫曼中没有结点的度为1 4)n个叶子结点的哈夫曼的结点总数为2n-1,其中度为2的结点数为n-1。 哈夫曼的应用之一是解决编码问题。在编码中,我们使用二进制来表示字符,其中固定长度编码和前缀编码是两种常见的编码方式。前缀编码要求没有一个编码是另一个编码的前缀,而哈夫曼构造的编码正是满足前缀编码的要求。 所以,哈夫曼数据结构考研中是一个重要的概念,它可以用于构造最优二叉和解决编码问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [数据结构考研笔记(十六) ——哈夫曼、编码应用](https://blog.csdn.net/sf9090/article/details/109154652)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值