数据结构之树与二叉树


在开始本文前,还是通过一张思维导图来了解下本文主要涉及的到的内容。
在这里插入图片描述

逻辑结构

在开始本文前,先说一下逻辑结构,同样用思维导图来了解。
在这里插入图片描述
本次主要内容就是树结构,线性结构在之前的文章中已经有了说明,图结构将在以后的文章中进行说明。

树的基本概念

树是n个节点的有限集合T,它或者为空,或满足以下条件:

  • 有且仅有一个特定的称为的节点;
  • 其余节点分为m个互不相交的子集,其中每个子集是根的子树
    树是递归的数据结构,根据下图来看一下这棵树。
    在这里插入图片描述
    在这棵树里,A就是根节点,而B和C是A的孩子节点,DEF就是A的子孙节点。
    n个节点的树中有n-1条边。如图中6个节点的树就有5条边,也就是除了根节点之外,其余每个节点都有一条边连向其他节点。

树的基本术语

用一张图来说明树的基本术语。
在这里插入图片描述

  • 树的结点:像A、B这种称为树的结点,其中A是根结点。
  • 结点的度和树的度:类似于B有两个孩子,那么B结点的度就是2,树的度是取结点度最大的值。
  • 叶子结点:度为0的结点,也就是没孩子的结点,图中E、F、G就是叶子结点。
  • 孩子结点:G是D的孩子结点。
  • 双亲结点:D是G的双亲结点。
  • 兄弟结点:同属于一个双亲,比如D、E都是B的孩子结点,那么D和E就是兄弟结点。
  • 堂兄弟结点:同属于一个层,比如E、F都在第三层,那么E和F就是堂兄弟结点。
  • 子孙结点:G是B的子孙结点。
  • 祖先结点:B和A都是G的祖先结点。
  • 路径与路径长度:可以看成边的个数,比如从A到G,边有三条,那么路径长度就是3。
  • 结点的层次和树的高度:A在第一层,B和C在第二层,以上往下就是层次。树的高度就是最大层次。
  • 有序树和无序树:有序树就是树中任意节点的子节点之间有顺序关系,无序树就是没有顺序关系。如果想更清楚的了解这个可以看一老哥的博文,我把地址贴出来。有序树和无序树的区别
  • 森林:m棵不相交的树的集合,如下图,我们就可以把这个叫做森林。它是由A节点为根的树和H为根的树组合而成的。
  • 在这里插入图片描述

树的性质

还是以之前的图来看。
在这里插入图片描述
1.树中的节点数=所有节点的度数(树中的边数)+1,这个就不用说明了,之前也表示过了。
2.度为m的树中第i层至多有mi-1个结点,这也比较好理解,如果度为2的树,那么就是二叉树,可以想象,二叉树第一层最多1个,第二层最多两个,那么第i层最多就是mi-1个了。
3.高度h的m叉树至多有(mh-1)/(m-1)个结点。这其实就是等比数列求和公式,第一层1个,第二层2个,第三层4个,那么第h层就是mh-1个,等比数列求和公式a1*(1-qn)/(1-q)。所以把a1=1代入,q=m代入。所以得到(1-mh)/(1-m),就是结论了。
4.具有n个结点的m叉树的最小高度为, ⌈ \lceil logm(n(m-1)+1) ⌉ \rceil ,这里其实上就是另把n= m h − 1 m − 1 \frac{m^h -1}{m-1} m1mh1,然后就可以通过这个得到h的值,但是这考虑的是满m叉树,要是它没有取满的话,就会出现类似2.多少这样小数点,但是它还是一共三层,所以要向上取整。

树小结

通过一个题目来结束本小节的内容。
【例】在一棵度为4的树T中,若有20个度为4的结点,10个度为3的结点,1个度为2的结点、10个度为1的结点,则树T的叶结点的个数是____。
解:已知这棵树的度为4,所以4是最高的了,那么它一共有n= 20 ∗ 4 + 10 ∗ 3 + 1 ∗ 2 + 10 ∗ 1 + 1 20*4+10*3+1*2+10*1+1 204+103+12+101+1,这里算的是边的个数+1=结点个数,除了这个我们还知道把所有结点加起来也是结点个数 n = 20 + 10 + 1 + 10 + n 0 n=20+10+1+10+n_0 n=20+10+1+10+n0,这里的n是结点总和,结合两个公式就可以得到 n 0 = 82 n_0=82 n0=82

二叉树

二叉树可以说是最多使用的一种树的结构了,二叉树是可为空的n个节点的有限集,由一个根结点及两棵互不相交的左子树右子树构成。通过下图,我们可以看下二叉树的五种形态。
在这里插入图片描述
二叉树就是度为2的树。

特殊二叉树

二叉树中有几种特殊情况的二叉树,分别是满二叉树,完全二叉树,二叉排序树和平衡二叉树。接下来将通过下文来一一介绍这些。

满二叉树

概念:除叶子结点外,其余结点度都为2。
特点:
1.若高度为h,则共有 2 h − 1 2^h-1 2h1个结点。不难理解,本来m叉树的结点个数是 ( m h − 1 ) ( m − 1 ) \frac{(m^h-1)}{(m-1)} (m1)(mh1),只需要将2代入就可以得到,思想都差不多,等比数列求和。
2.对编号为i的结点,若存在双亲,则双亲编号为 ⌊ i 2 ⌋ \lfloor\frac{i}{2}\rfloor 2i,左孩子为2i,右孩子为2i+1。
来看下下面这个满二叉树的图,大家可以通过下图来自己算下两个特点是不是对的就可以更好的理解了。
在这里插入图片描述

完全二叉树

概念:若完全二叉树共n个结点,那么每个结点与深度为k的满二叉树的1-n个结点编号都相同。
特点:
1.叶子结点只能在最后一层,且以此排在最左边的位置上。
2.最多只能有一个度为1的结点。
通过下图可以了解下完全二叉树。
在这里插入图片描述
如果说没有图中的5,那就出现了有两个度为1的结点,这是不符合完全二叉树的特点的。并且也不能满足叶子结点都在最左边上。

二叉排序树

概念:对任意结点若存在左子树/右子树,则左子树上所有结点的关键字均小于该结点,右子树上所有结点的关键字均大于该结点,左子树和右子树又是一棵二叉排序树(递归)。来看下下图。
在这里插入图片描述
可以发现8左边的都是小于8,8右边的都是大于8。二叉排序树还是比较好理解的。

平衡二叉树

概念:树上任意结点的左子树和右子树的深度之差不查过1。还是用图来说明。
在这里插入图片描述
这里8的左子树是3,8的右子树是2,所以3-2 是没有超过1的。假设去掉10这个结点,那么左边就是3,右边是1,3-1=2是超过1,所以就不是平衡二叉树了。

二叉树的性质

1.二叉树第i层上结点数最多为 2 i − 1 2^{i-1} 2i1个。比如第一层1个,第二层最多2个,所以就很容易推出来。
2.深度为k的二叉树至多有 2 k − 1 2^k-1 2k1个结点。就是等比数列求和公式,上面也有提到。
3.对任意一棵二叉树,若叶子结点数是 n 0 n_0 n0,度为2的结点数为 n 2 n_2 n2,则 n 0 = n 2 + 1 n_0=n_2+1 n0=n2+1。我们知道结点数= n 0 + n 1 + n 2 n_0+n_1+n_2 n0+n1+n2,并且结点数=边数+1= 1 ∗ n 1 + 2 ∗ n 2 + 0 ∗ n 0 1*n_1+2*n_2+0*n_0 1n1+2n2+0n0。联合两个公式得到 n 0 = n 2 + 1 n_0=n_2+1 n0=n2+1
4.具有n个结点的完全二叉树的深度为 ⌊ \lfloor log 2 _2 2n ⌋ + 1 \rfloor+1 +1。这个公式其实可以根据 ⌈ \lceil logm(n(m-1)+1) ⌉ \rceil 来得到,将m=2代入。

二叉树的存储结构

二叉树可以分为顺序存储跟链式存储,不过我们一般都是用链式存储的。

顺序存储

假设有下图二叉树。
在这里插入图片描述
那么存储到数组里如下标。

0
1A
2B
3C
40
5D
从上到下,从左到右依次存入,没有结点就写0。

链式存储

我们知道二叉树有左子树和右子树,还有结点数据,所以产生了下图的结点结构。
在这里插入图片描述
用代码来定义结构体如下

typedef struct BTNode
{
	ElemType data;
	struct BTNode *lchild;
	struct BTNode *rchild;
}BTNode;

在这里插入图片描述
那么上图二叉树的存储应该是如下图所示。
在这里插入图片描述

二叉树的遍历

二叉树的遍历:沿某条搜索路径寻访二叉树,对每个结点访问一次且仅访问(读、写、修改和输出结点信息等)一次,使得非线性排列变成某种意义上的线性序列。
那么二叉树的遍历一般有三种
1.先序遍历,先访问根再访问左子树,最后右子树。
2.中序遍历,先访问左子树,再访问根,最后访问右子树。
3.后序遍历,先访问左子树,再访问 右子树,最后访问根。
其实这三种遍历都是按照根在哪个位置而取名的遍历。

先序遍历

按照先序遍历访问下图二叉树。
在这里插入图片描述
那么先访问根,所以第一次结束结果为A。
再访问A的左子树,B的左子树的根为B,所以序列现在为AB。
再访问B的左子树,也就是D,那么现在序列是ABD。
D没有左子树,所以访问D的右子树G,现在序列是ABDG。
这时候B的左子树都访问完毕,就访问B的右子树E,现在序列是ABDGE。
E没有左右子树,所以这部操作结束后,A的左子树全部访问完毕,那么接下来访问A的右子树。
访问C,序列是ABDGEC,C没有左子树,所以只能访问F,最后序列就是ABDGECF。
先序遍历也可以叫做先根遍历,下面是先序遍历的代码:

void preorder(BTNode *T){
	if(T!=NULL){
		visit(T->data);
		preorder(T->lchild);
		preorder(T->rchild);
	}
}

这里我就先把中序和后序的代码都写在这了,因为只是改变visit的位置即可,这里用到的是递归,递归在之前栈应用的文章中已经讲过,有兴趣的朋友可以自己模拟下这个递归的过程,这里就不多过多讲述。
中序遍历代码:

void inorder(BTNode *T){
	if(T!=NULL){
		preorder(T->lchild);
		visit(T->data);
		preorder(T->rchild);
	}
}

后序遍历代码:

void postorder(BTNode *T){
	if(T!=NULL){
		preorder(T->lchild);
		preorder(T->rchild);
		visit(T->data);
	}
}

中序遍历

图还是之前,那张为了方便看,我把它复制下来。
在这里插入图片描述
中序遍历是先访问左子树,那么先访问A的左子树,也就是B这棵,发现B还有左子树D,那么再访问D,D没有左子树了,所以现在序列是D。
访问完D了之后就得访问D的右子树G,所以现在序列是DG。
那么B的左子树全部访问完毕,这时候就访问B,所以序列是DGB。
访问完B就得访问B的右子树就是E,访问结束后序列为DGBE。
现在A的左子树全部访问完毕,就访问A,序列为DGBEA。
A访问完就访问A的右子树C,发现C没有左子树,所以直接访问C,那么现在序列是DGBEAC。
最后再访问C的右子树F,得到最终序列DGBEACF。
大家可以自己去模拟一下这个过程。

后序遍历

图还是上面那张,把它拿下来。
在这里插入图片描述
后序遍历是先访问左子树和右子树,最后才访问根节点。
先访问A,发现A有左子树B,访问B,发现B有左子树D,访问D,发现D没有左子树,但是D有右子树G,G没有左右子树,那么序列:G。
G访问完就回到根D,访问D,序列:GD。
访问完D回到B,发现B有右子树,那么访问E,序列:DGE。
访问完E可以回到B了,访问B,序列:DGEB。
访问完B,回到A,发现A还有右子树,那么访问A的右子树C,C是没有左子树,那就访问C的右子树F,序列:DGEBF。
访问完F回到C,访问C,序列:DGEBFC。
最户访问根节点A,序列:GDEBFCA。

层次遍历

二叉树还有层次遍历,从名字可以看出,是一层一层访问,那么这时候就需要一个队列来实现层次访问。拿下图来说。
在这里插入图片描述
思想:先把A插入队列,然后弹出A到序列,找与A连接的压入队列,也就是B和C。弹出B,把B连接的DE压入队列,弹出C,把与C相连的F压入队列,依次弹出D、E、F,然后跟上面操作一样,最后就得到序列:ABCDEFG。
实现代码如下:

#define MAX_NODE 50
void levelorder(BTNode *T){
	BTNode *Queue[MAX_NODE],*p=T;//要声明一个指针指向根结点
	int front=0,rear=0;
	if(p!=NULL){
		Queue[rear++]=p;//一开始将根结点先压入队列
		while(front<rear){//如果队列里有结点
			p=Queue[++front];//弹出结点
			visit(p->data);//访问弹出结点的数据
			if(p->lchild!=NULL){//左孩子不为空就压入队列
				Queue[++rear]=p->lchild;
			}
			if(p->rchild!=NULL){//右孩子不为空就压入右孩子
				Queue[++rear]=p->rchild;
			}
		}
	}
}


线索二叉树

在二叉树中,你可能会发现特别多的指针空着,因为它们可能没有左孩子和右孩子。如下图。
在这里插入图片描述
那么n个结点的二叉树采用链式存储结构时,有n+1个空链域。这个n+1怎么得来的呢。首先n个结点的二叉树有2n个链域,一共有n-1个链域被使用(可以理解为n-1条边),所以空链域就是 2 n − ( n − 1 ) = n + 1 2n-(n-1)=n+1 2n(n1)=n+1
这时候就会引入线索二叉树,下面是线索二叉树的结点结构。
在这里插入图片描述
下面是代码定义的结构体:

typedef struct BiThrNode{
	TElemType data;
	struct BiThrNode *lchild;
	struct BiThrNode *rchild;
	int LTag;
	int RTag;
}DLinkNode;

线索二叉树的引入加快了查找结点前驱和后继的速度,对二叉树以某种次序遍历使其变为线索二叉树的过程称为线索化。
下面还是通过一张图来了解下线索二叉树。
在这里插入图片描述
在线索二叉树中,引入了一个头指针b,用来指向二叉树的头结点。线索二叉树的先序、中序和后序分别如下图。
先序:
在这里插入图片描述
这里我们可以知道B是没有左孩子的,所以B的LTag应该是1,指向它的前驱A。从图中虚线可以看出来。
中序:
在这里插入图片描述
同样在这里B是没有左孩子的,但是中序B是没有前驱的,所以指向NULL,其余同理。
后序:
在这里插入图片描述
这里B就有前驱了是D,所以它指向D。

这些都需要自己都过模拟去理解,多动手理解起来还是很快的。

树、森林、二叉树的转换及遍历

树与二叉树的转换

将树转换为二叉树,就是让它的兄弟成为它的右孩子,然后去掉本来的连线。拿下面这个二叉树举例。
在这里插入图片描述
那么,先让C变成B的右孩子,E和F变成D的右孩子。如下图。
在这里插入图片描述
那么,树转换为二叉树就完成了。

二叉树转换为树

与前面的树转换为二叉树反一下就行了,将右孩子变成兄弟结点,去掉连线。
在这里插入图片描述
那么这里就是要把D的右孩子,变为D的兄弟,也就是D、E和F属于同一层,都是B的孩子,B和C属于同一层,都是A的孩子,然后去掉之前的连线。得到如下图。
在这里插入图片描述

森林与二叉树的转换

1.森林中每棵树转成二叉树
2.后一棵树做前一棵树根节点的右子树。
如下图森林。
在这里插入图片描述
首先,需要把这三棵树从树转换为二叉树,转化结果如下。
在这里插入图片描述
然后D变成A的右孩子,F变成D的右孩子,最后二叉树如下。
在这里插入图片描述

二叉树转换成森林

怎么把二叉树变成森林呢,有两个步骤
1.断掉右孩子
2.二叉树变成树
在这里插入图片描述
从图中看,首先得断掉A的右孩子,也就是D这一块,然后断D的右孩子,也就是F以下。接下来把断掉之后的二叉树转换为树,就得到下图结果。
在这里插入图片描述

遍历

在前面已经讲过了二叉树的遍历,接下来是树的遍历和森林的遍历,用下面的思维导图来熟悉下内容。
在这里插入图片描述

树的遍历

先来看树的遍历

树的先根遍历

先根遍历:先访问树的根节点,再依次先根遍历根的每棵子树。拿下图来举例子。
在这里插入图片描述
那么先访问A,访问完A后遍历A的每棵子树,所以先遍历B的子树,访问B,得到序列:AB。
再访问B的子树D,得到序列:ABD。
访问D的子树G,得到序列:ABDG。
这时候B的一棵子树访问完了,那么就访问E这棵,得到序列:ABDGE。
访问完E访问F这棵,得到序列:ABDGEF。
最后访问A的另一棵子树C,得到最终序列ABDGEFC。

树的先根和树对应二叉树的先序

有上图中的树可以转换成二叉树,转换完成的二叉树如下图。
在这里插入图片描述
那么这棵二叉树的先序遍历序列是:ABDGEFC。
所以得到结论:树的先根遍历序列与这棵树对应的二叉树的先序遍历的结果相同。

树的后根遍历

在这里插入图片描述
后根遍历:先依次后根遍历根的子树,再访问根结点。
按照这个所以应该是从D的子树开始访问,从G->D->E->F->B->C->A。还是比较容易的就不多说了。

树的后根遍历与对应二叉树的中序遍历

同样是这根树对应的二叉树。
在这里插入图片描述
它的中序序列是GDEFBCA。
得出结论:树的后根遍历序列与这棵树对应的二叉树的中序遍历的结果相同。

森林的遍历

森林的先序遍历

若森林不为空,则
1.访问森林中第一棵树的根结点。
2.先序遍历森林中第一棵树的子树结点。
3.先序遍历森林中(除第一棵树之外)其余构成的森林。
拿下面的图举个例子。
在这里插入图片描述
那么首先先遍历第一棵树,也就是A这棵。采用的是先序,所以得到的序列是:ABC。
然后访问第二棵树,那么访问完序列是ABCDE。
最后先序访问第三棵树,得到最终序列ABCDEFGHI。

森林与对应二叉树的先序遍历

下图森林对应的二叉树如下下图。
在这里插入图片描述
在这里插入图片描述
之前得到的森林序列是:ABCDEFGHI。
转换后的二叉树的二叉树的先序遍历之后得到的序列是:ABCDEFGHI。
所以得到结论:森林的先序遍历序列与森林对应的二叉树的先序遍历的结果相同。

森林的中序遍历

若树不为空,则
1.中序遍历森林中第一棵树的子树森林。
2.访问森林中第一棵树的根结点。
3.中序遍历森林中(除第一棵树之外)其余树构成的森林。
在这里插入图片描述
首先访问第一棵树,那么中序得到的结果是BCA。
然后就是访问第二棵树,那么是ED,所以序列就是BCAED。
最后访问第三棵树,得到第三棵树的GIHF。最终BCAEDGIHF。

森林与对应二叉树的中序遍历

从前面已经有森林和对应二叉树。
那么对应二叉树的中序序列是:BCAEDGIHF。
得到的结论:森林的中序遍历序列与森林对应的二叉树的中序遍历的结果相同。

小结

通过一道题目来回顾下本章节的知识点吧。
【例】若森林F有15条边、25个结点,则F包含树的个树____。
解:一共有25个结点,所以边树应该是n-1,也就是24条边。那么F中有15条,所以用24-15=9条,还剩下9条边用来连成二叉树了,那么一共的树应该是9+1,就是10棵。
注:这题的知识点就是用森林变成二叉树,那么就需要将原本的转换为二叉树,再将接下来的连成它的右子树,那么两棵就需要一条边。所以n棵需要n-1条边,题目中多出9条边,所以有10棵。这里树转为二叉树虽然有删边的情况,但是删了后还是补上了,所以这里不变。

树的应用

二叉排序树

在前面已经介绍过二叉排序树了,就是左边的小于根,右边的大于根。
那么下列数字:45,24,53,12,24,90。把它们组合成一个二叉排序树如何组合呢。
首先45当做根结点,24比45小,所以在左边,53比45大,所以在右边。三个结点插入后如下图。
在这里插入图片描述
后面12比45小,插左边,发现12比24小,那么成为24的左孩子。24比45小,但是下面一个也是24,发现重复了,所以不必把这个24插入,最后就是90,90比45大,也比53大,所以是插在53的右孩子。最后结果如下图。
在这里插入图片描述
这时候,用中序遍历来遍历这个序列,结果是12,24,45,53,90。所以用中序遍历可以得到一个关键字的有限序列

二叉排序树的平均查找长度

平均查找长度ASL的计算是每个结点的查找长度除以结点的个数。拿下图来说。
在这里插入图片描述
这里一共是9个结点。第一层1个结点,查找长度1,第二层两个,查找长度 2 ∗ 2 2*2 22,第三层就是 3 ∗ 3 3*3 33,第四层 3 ∗ 4 3*4 34。加起来除以9得到平均查找长度。
接下来通过一道题目来感受下二叉排序树
【例】对于下列关键字序列,不可能构成某二叉排序树中的一条查找路径的是____。
A.95,22,91,24,94,71 B.92,20,91,34,88,35
C.21,89,77,29,36,38 D.12,25,71,68,33,34
解:就直接看A吧。首先是95成为根结点,22成为95的左孩子,91成为22的右孩子,24成为91的左孩子,这时候进入了94,它首先应该是95左边,然后比22大,那么应该是22的右边,它只能插到24的右边,但是24是91的左孩子,应该左边的都比91小,但是94比91大了,所以A不可能。

平衡二叉树AVL

概念:树上任意结点的左子树和右子树的深度之差不超过1.(AVL一定是二叉排序树)
下面通过一张图来看下。
在这里插入图片描述
这里根节点是8,那么看下8左边,深度为3,8右边深度为2,8这个结点的平衡因子是1。(这里出现了平衡因子这个概念,就是左子树深度-右子树深度就是平衡因子)。

平衡二叉树调整的方法

其实在很多教科书或者参考书上,都是LL,LR,RR或者RL这样。其实可以通过四点来更加方便来调整。
1.找到失衡的最小子树
2.从该子树根节点到插入结点的路径上,取前三个结点。
3.把三个结点中间值作为新的根结点
4.继续调整。
这里没涉及到代码,就涉及到如何取调整,以下图为例。
在这里插入图片描述
算一下16这个结点的平衡因子,它左子树深度是2,右子树深度是0,所以平衡因子是2,所以得调整它,那么从16开始的就是最小失衡子树。11是中间值,所以进行调整后如下图。
在这里插入图片描述
下面通过两道题目来巩固一下这个知识点。
【例】若将关键字1,2,3,4,5,6,7依次插入到初始为空的平衡二叉树中,则T中平衡因子为0的分支结点的个数是____。
解:1作为根结点,2作为1的右孩子,这时候还是平衡的,插入3,3是2的右孩子就不平衡了如下图。
在这里插入图片描述
1的平衡因子是2,所以得调整,那么2是中间值,所以调整后为下图。
在这里插入图片描述
接下来插入4,那么4是3的右孩子,插入5,5是4的右孩子,又导致3不平衡。如下图。
在这里插入图片描述
所以这时候要调整,那么就把4作为中间结点,调整后如下图。
在这里插入图片描述
插入6,是5的右孩子,这时候又不平衡了,看下图。
在这里插入图片描述
这时候2的平衡因子是1-3=-2。所以得调。那么找到失衡子树是从2开始的,取三个结点,那就是2、4、5,将4作为中间结点变成根结点。然后调整后的结果如下图。
在这里插入图片描述
这里注意3要插入到2的右边取,而不是5的左边,因为3比4小。
最后插入7,是6的右孩子,那么5就不平衡了,跟上面一样,把6当做中间结点。取5、6、7,调整,最后结果如下图。
在这里插入图片描述
那么可以算一下,T中平衡因子为0的分支结点,是4,2和6。那么就是3个。

【例】若平衡二叉树的高度为6,且所有非叶子结点的平衡因子均为1,则该平衡二叉树的结点总数为_____。
解:当只有一个结点的时候平衡因子是0,当结点是2个的时候,高度是2。当高度是3个时候,结点数是4个。当高度是4的时候,结点数是7个,得出规律,高度是N的,且所有非叶子结点的平衡因子均为1的公式 T N = T N − 1 + T N − 2 + 1 T_N=T_{N-1}+T_{N-2}+1 TN=TN1+TN2+1。其实就是当前是前两个的组合再加上一个结点就可以达到题目目的。本题当然也可以慢慢画图也可以解答,最后带入N=6,得到结点个数为20。

哈夫曼树

哈夫曼树:最优树,是带权路径长度WPL最短的树。
若给定权值分别是2、3、6、7,具有4个叶子结点的二叉树,则它们的带权路径长度分别为:
在这里插入图片描述
W P L = 2 ∗ 2 + 3 ∗ 2 + 6 ∗ 2 + 7 ∗ 2 = 36 WPL=2*2+3*2+6*2+7*2=36 WPL=22+32+62+72=36
在这里插入图片描述
W P L = 47 WPL=47 WPL=47
在这里插入图片描述
W P L = 7 ∗ 1 + 6 ∗ 2 + 2 ∗ 3 + 3 ∗ 3 = 34 WPL=7*1+6*2+2*3+3*3=34 WPL=71+62+23+33=34

构造哈夫曼树

哈夫曼树性质:
1.每个初始结点最后都成为叶子结点,权值越大的结点,离根节点越近。
2.树中无度为1的结点
3.哈夫曼树中结点总数为 2 n 0 − 1 2n_0-1 2n01,且WPL最短。(这个2n-1可以通过公式自己推算,因为知道度为0的结点个数是初始结点的个数,总结点个数 n = n 0 + n 2 n=n_0+n_2 n=n0+n2,还知道是边数+1,那么就是 n = 2 n 2 + 1 n=2n_2+1 n=2n2+1,联立一下就可以得到)
若给定权值分别为8、3、4、6、5、5来构造一棵哈夫曼树。
那么用上述进行构造,先用3和4,5和5,如下图。
在这里插入图片描述
然后用6去跟3和4的和,用8去跟5和5的和,如下图。
在这里插入图片描述
得到这个哈夫曼树。假设用a、b、c、d、e、f来表示图中的树。并且用0和1来表示它们的左边和右边,如下图。
在这里插入图片描述
所有左孩子的线用0表示,所有右孩子的线用1表示,那么图中d就是00,b就是010。其它类似。我们可以发现下面两个特点
1.使用频率越高的字符采越短的编码。比如a是8,它的编码是10。
2.一个字符的哈夫曼编码不可能是另一个字符的哈夫曼编码的前缀。不然就会混乱,无法区分。

最后的最后,还是用题目来回顾下哈夫曼编码
【例】已知字符集{a,b,c,d,e,f},若各字符出现的次数分别为6,3,8,2,10,4。则对应字符集中各字符的哈夫曼编码可能是。
解:这里我们求需要画出它的哈夫曼树,如下图。
在这里插入图片描述
那么用a、b、c、d、e、f来代替其中文字,所以a是6是00,剩下的大家可以自己去写了。

总结

之前还说物理层是自己写过最长的一篇文章,这篇比物理层更长哈哈,不过通过写这篇文章,自己对树的公式推导或者树的知识有了更加好的了解,在二叉树的先序,中序和后续遍历中,只用了递归的写法,非递归写法是通过队列实现的,到时候应该会单独开一篇文章来说明。如果读者对本文中内容有什么问题欢迎在评论区交流,我会及时改正和回复。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值