《数据结构》学习系列——树(下)

系列文章目录

目录


树和森林的遍历

树的遍历

  • 先根遍历:先访问树的根结点,然后依次先根遍历每颗子树
  • 后根遍历:先依次后根遍历每颗子树,然后访问树的根结点

森林的遍历

  • 先根遍历森林:
    • 访问森林中第一棵树的根节点
    • 先根遍历第一棵树中的诸子树
    • 先根遍历其余的诸树(森林)

森林的先根遍历与其对应的二叉树先根遍历相同

  • 后根遍历森林:
    • 后根遍历第一棵树的诸子树
    • 访问森林中第一棵树的根节点
    • 后根遍历其余诸树(森林)

森林的后根遍历序列与其对应的二叉树的中根遍历序列一致

基本算法

递归先根遍历树

PreOrder(t)
{
	if t == NULL return;
	print(Data(t));
	GFC(t,child); // 找到t的第一个子节点
	while chile != NULL
	{
		PreOrder(child);
		GNB(child,child);
	}
}

迭代先根遍历树

思路

(1)若结点p不为空,访问结点p,将结点p压入栈,并将其大儿子结点设为结点p;反复执行(1),直至结点p为空

(2)从栈中弹出一个结点,将其设为结点p,若它有大兄弟结点,则将其大兄弟结点压入栈,且将该兄弟结点设为结点p;否则,反复执行(2),直

至弹出的结点有大兄弟结点或栈空以至无结点可弹出

(3)反复执行(1)和(2),直至栈为空

伪代码

NPO(t)
{
	create(s);
	p = t;
	bool flag = True;
	while flag or S非空
	{
		flag = False;
		while p != NULL;
		{
			print(Data(p));
			Push(S,p);
			p = FierstChlid(p)
		}
		while p != NULL and S 非空;
		Pop(S,p);
		p = NextBrother
	}
}

树和森林的层次遍历

LevelOrder(t)
{
	CREATE(Q);
	Q = t;	// 根节点入队列
	while not IsEmpty(Q)
	{
		p = Q;
		while p != NULL
		{
			print(Data(p));
			if FirstChild(p) != NULL  Q = FirstChild(p);
			p = NextBrother(p);
		}
	}
}

压缩与哈夫曼树

文件编码

  • 假设有一个文件仅包含7种字符:a、e、i、s、t、sp(空格)和nl(换行),且文件中有10个a,15个e,12个i,3个s,4个t,13个sp,1个nl

  • 因为「 log ⁡ 2 7 \log_27 log27]=3,所以,每个字符都至少由一个3位的二进制串表示。于是文件的总位数至少应该是:10×3+15×3+12×3+3×3+4×3+13×3+1×3=174

  • 在实际应用的一些大文件中,字符被使用的比率是非平均的,即有些字符出现的次数较多,而有些字符出现的次数却非常少。

    如果所有字符都由等长的二进制码表示,将会造成空间浪费

  • 如何才能减少不必要的空间浪费

  • 文件压缩的通常策略:采用不等长的二进制码,令文件中出现频率高的字符的编码尽可能短

    • 采用不等长编码又可能会产生多义性。例如:如果用01表示a,10表示b,1001表示c,那么对于编码1001,我们无法确定它表示字符c,还是表示字符串ba,其原因是b的编码与c的编码的开头(前缀)部分相同
    • 为避免出现多义性,必须要求字符集中任何字符的编码都不是其它字符的编码的前缀,满足这个条件的编码被称为前缀码。显然,等长编码是前缀码
  • 怎样的前缀码才能使文件的总编码长度最短

    • 设组成文件的字符集A={a1,a2,...,an},其中,a的编码长度为 I i I_i Ii;a出现的次数为 c i c_i ci。要使文件的总编码最短,就必须要确定 I i I_i Ii,使得取最小值
      ∑ i = 0 n c i I i \sum_{i=0}^nc_iI_i i=0∑nciIi
  • 如何设计出总编码最短的前缀码

    • 哈夫曼算法

扩充二叉树

定义

为了使问题的处理更为方便,每当原二叉树中出现空子树时,就增加特殊的结点------空树叶,由此生成的二叉树称为扩充二叉树

  • 扩充二叉树中每个圆形结点都有两个子结点,每一个方形结点都没有子结点
  • 规定空二叉树的扩充二叉树为只有一个方形结点。以下称圆形结点为内结点,方形结点为外结点0
  • 扩充二叉数的外通路长度 定义为从根到每个外结点的路径长度之和,内通路长度定义为从根到每个结点的路径长度之和
  • 给扩充二叉树的n个外结点分别赋上一个实数权。扩充二叉树的加权外通路长度 定义为:
    W P L = ∑ i = 0 n w i L i WPL = \sum_{i=0}^nw_iL_i WPL=i=0∑nwiLi

    其中n表示外结点的个数, w i w_i wi和 L i L_i Li分别表示外结点 k i k_i ki的权值和根到 k i k_i ki的路径长度

  • 在外结点权值分别为 w 0 , w 1 , . . . , w n − 1 w_0,w_1, ... ,w_{n-1} w0,w1,...,wn−1的所有扩充二叉树中,加权外通路长度最小的扩充二叉树称为最优二叉树'

哈夫曼树和哈夫曼编码

  • 文件编码问题就变成构造最优二叉树问题,每个外结点代表一个字符,其权值代表该字符的频率,从根到外结点的路径长度就是该字符的编码长度
  • 为求得最优二叉树,哈夫曼巧妙的设计了哈夫曼算法,通过哈夫曼算法可以建立一棵哈夫曼树,从而为压缩文本文件建立了哈夫曼编码
哈夫曼树的基本思路
  1. 根据给定的n个权值 w 1 w_1 w1, w 2 w_2 w2,..., w n w_n wn构成n棵二叉树的森林F={ T 1 , T 2 , ... , T n T_1,T_2,...,T_n T1,T2,...,Tn},其中每棵二叉树 T i T_i Ti中都只有一个权值为 w i w_i wi的根结点,其左、右子树均为空
  2. 在森林F中选出两棵根结点权值最小的树作为一棵新树的左、右子树,且置新树的根结点的权值为其左、右子树上根结点的权值之和
  3. 从F中删除构成新树的那两棵树,同时把新树加入F中
  4. 重复第2和第3步,直到F中只含有一棵树为止,此树便是哈夫曼树

例子

哈夫曼编码

将哈夫曼树每个分支结点的左分支标上0,右分支标上1,把从根结点到每个叶结点的路径上的标号连接起来,作为该叶结点所代表字符的编码,这样得到的编码称为哈夫曼编码

  • 定理:在外结点权值分别为 w 0 , w 1 , ... , w n − 1 w_0,w_1,...,w_{n-1} w0,w1,...,wn−1的扩充二叉树中,由哈夫曼算法构造出的哈夫曼树的带权路径长度最小,因此哈夫曼为最优二叉树
  • 根据该定理可知,对于所有的编码,哈夫曼编码使文件的总编码长度最短。实际上,哈夫曼算法的应用广,这里只是以哈夫曼编码为例来说明哈夫曼算法。由观察可知,字符集中的字符所在的结点均是哈夫曼树中的外结点
  • 哈夫曼树中没有度为1的结点

示例

在构造哈夫曼树的过程中,没有一片树叶是其他树叶的祖先,所以每个叶结点对应的编码不可能是其他叶结点对应的编码前缀。由此可知哈夫曼编码是二进制的前缀码

哈夫曼树中每个结点的结构

其中,LLINK和RLINK为链接域,INFO为信息域,Weight为全值

伪代码

Huffman(H,m,t)
{
	for (i = 1; i<=m;i++)
	{
		LLINK(H[i]) = RLINK(H[i]);
		RLINK(H[i]) = NULL;
	}
	for (i = 1,i<m;i++)
	{
		t = new;
		P1 = H[i];
		P2 = H[i+1];
		Weight(t) = Weight(P1) + Weight(P2);
		LLINK(t) = P1;
		RLINK(t) = P2;
	}
	j = i+2;
	while (Weight(t)>Weight(H[j]))
	{
		H[j-1] = H[j];
		j = j+1;
		H[j-1] = t;
	}
}
相关推荐
就爱敲代码15 分钟前
怎么理解ES6 Proxy
1024程序员节
憧憬一下15 分钟前
input子系统的框架和重要数据结构详解
arm开发·嵌入式·c/c++·1024程序员节·linux驱动开发
三日看尽长安花24 分钟前
【Tableau】
1024程序员节
sswithyou44 分钟前
Linux的调度算法
1024程序员节
武子康1 小时前
大数据-187 Elasticsearch - ELK 家族 Logstash Filter 插件 使用详解
大数据·数据结构·elk·elasticsearch·搜索引擎·全文检索·1024程序员节
互联网杂货铺1 小时前
Python测试框架—pytest详解
自动化测试·软件测试·python·测试工具·测试用例·pytest·1024程序员节
GDAL2 小时前
JavaScript正则表达式利器:exec()方法深度解析与应用实例
正则表达式·1024程序员节
2401_857610032 小时前
植物健康,Spring Boot来助力
1024程序员节
阿乾之铭2 小时前
Spring Boot框架中的IO
java·spring boot·log4j·1024程序员节
百流2 小时前
Pyspark中pyspark.sql.functions常用方法(4)
1024程序员节