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

系列文章目录

目录


树和森林的遍历

树的遍历

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

森林的遍历

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

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

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

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

基本算法

递归先根遍历树

复制代码
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;
	}
}
相关推荐
学传打活3 天前
【边打字.边学昆仑正义文化】_进阶篇5 宇宙星系的运转(续3)
微信公众平台·1024程序员节·汉字·昆仑正义文化
学传打活8 天前
【边打字.边学昆仑正义文化】_进阶篇5 宇宙星系的运转(续1)
微信公众平台·1024程序员节·汉字·昆仑正义文化
fangzhanpeng16816 天前
快速掌握C#语言基础知识点(1.基础数据类型)
开发语言·c#·1024程序员节
无己心18 天前
面向多平台 AI 搜索引擎的适配层架构设计
前端·人工智能·搜索引擎·ai·1024程序员节
u01030552724 天前
AI驱动的安卓UI自动生成功能实现
人工智能·1024程序员节
u01030552725 天前
长株潭智能体赋能腾讯元器链接共享
1024程序员节
云贝贝贝1 个月前
OceanBase认证体系介绍与备考指南
运维·服务器·oceanbase·1024程序员节
u0103055271 个月前
Java图像处理实战指南
人工智能·1024程序员节
u0103055271 个月前
Java AWT鼠标事件全解析
人工智能·1024程序员节
u0103055271 个月前
Java实用类应用精讲
人工智能·1024程序员节