Huffman算法学习(一)

1.基本思想

以字符的使用频率作为权构建一棵Huffman树,然后利用Huffman树对字符进行编码。构造一棵Huffman树,是将所要编码的字符作为叶子结点,该字符在文件中的使用频率作为叶子结点的权值,以自底向上的方式,通过n-1次的“合并”运算后构造出的一棵新树,核心思想是权值越大的叶子离根越近。

2.求解步骤

Huffman算法,采用贪心策略,每次从树的集合中取出没有双亲且权值最小的两棵树作为左右子树,构造一棵新树,新树根节点的权值为其左右孩子结点权值之和,将新树插入到树的集合中。

  1. 确定合适的数据结构。编写程序前需要考虑的情况有:
  2. 初始化。
  3. 如果T中只剩下一棵树,则Huffman树构造成功,跳到步骤(6)。否则,从集合T中取出没有双亲且权值最小的两棵树ti和tj,将它们合并成一棵新树zk,新树的左孩子为ti,右孩子为tj,zk的权值为ti和tj的权值之和。
  4. 从集合T中删去ti,tj,加入zk
  5. 重复以上3~4步。
  6. 约定左分支上的编码为“0”,右分支上的编码为“1”。从叶子结点到根结点逆向求出每个字符的Huffman编码,从根节点到叶子结点路径上的字符串为该叶子结点的Huffman编码;算法结束。

3.伪代码详解

在构造Huffman树的过程中,首先给每个结点的双亲、左孩子、右孩子的初始化为-1,找出所有结点中双亲为-1、权值最小的两个结点t1 ,合并为一棵二叉树,更新信息(双亲结点的权值为t1、t2权值之和,其左孩子为权值最小的结点t1,右孩子为次小的结点t2,t1、t2的双亲为双亲结点的编号)。重复此过程,构造一棵Huffman树。

(1)数据结构

每个结点的结构包括权值、双亲、左孩子、右孩子、结点字符信息这5个域。定义为结构体形式,定义结构体HnodeType:

typedef struct
{
	double weight;		//权值
	int parent;			//双亲
	int lchild;			//左孩子
	int rchild;			//右孩子
	char value;			//该结点表示的字符
} HNodeType;
weight parent lchild rchild value

在编码结构体中,bit[]存放结点的编码,start记录编码的开始下标,逆向编码;存储时,start从n-1开始依次递减,从后往前存储;读取时,从start+1开始到n-1,从前往后输出,即为该字符的编码。

typedef struct
{
	int bit[MAXBIT];	//存储编码的数组
	int start;			//编码开始下标
}HCodeType;				//编码结构体
0 1 1

其中start指针指向第四个哥,最后一个格即n-1

(2)初始化

初始化存放Huffman数组HuffNode[]中的结点

for(int i=0;i<2*n-1;i++)
{
	HuffNode[i].weight=0;		//权值
	HuffNode[i].parent=-1;	//双亲
	HuffNode[i].lchild=-1;		//左孩子
	HuffNode[i].rchild=-1;		//右孩子
}

Huffman树构建数组

weight parent lchild rchild value
0 5 -1 -1 -1 a
1 32 -1 -1 -1 b
2 18 -1 -1 -1 c
3 7 -1 -1 -1 d
4 25 -1 -1 -1 e
5 13 -1 -1 -1 f
6 0 -1 -1 -1
7 0 -1 -1 -1
8 0 -1 -1 -1
9 0 -1 -1 -1
10 0 -1 -1 -1

输出n个叶子结点的字符及权值:

for(int i=0;i<n;i++)
{
	cout<<"Please input value and weight of leaf node"<<i+1<<endl;
	cin>>HuffNode[i].value>>HuffNode[i].weight;
}

(3)循环构造Huffman树

从集合T中取出双亲为-1且权值最小的两棵树ti和tj,将它们合并成一棵树zk,新树的左孩子为ti,右孩子为tj,zk的权值为ti和tj的权值之和。

int i,j,x1,x2;								//x1,x2为两个最小权值结点的序号
double m1,m2;						//m1、m2为两个最小权值结点的权值
for(int i=0;i<n-1;i++)
{
	m1=m2=MAXVALUE;			//初始化为最大值
	x1=x2=0;							//找出所有结点中权值最小、无双亲结点的两个结点
	for(j=0;j<n+i;j++)
	{
		if(Huffman[j].weight<m1&&Huffman[j].parent==-1)
		{
			m2=m1;
			x2=x1;
			m1=HuffNode[j].weight;
			x1=j;
		}
		else if(HuffNode[j].weight<m2&&HuffNode[j].parent==-1)
		{
			m2=HuffNode[j].weight;
			x2=j;
		}
	}
	/*更新新树信息*/
	HuffNode[x1].parent=n+i;			//x1的父亲为新结点编号n+i
	HuffNode[x2].parent=n+i;			//x2的父亲为新结点编号n+i
	HuffNode[n+i].weight=m1+m2;	//新结点权值为两个最小权值之和m1+m2
	HuffNode[n+i].lchild=x1;				//新结点n+i的左孩子为x1
	HuffNode[n+i].rchild=x2;				//新结点n+i的右孩子为x2
}

(1)i=0时,j=0;j<6;找双亲为-1,权值最小的两个数:

x1=0 x2=3;				//x1、x2为两个最小权值结点的序号
m1=5 m2=7;				//m1、m2为两个最小权值结点的权值
HuffNode[0].parent=6;	//新结点n+i的左孩子为x1
HuffNode[3].parent=6;	//新结点n+i的左孩子为x2
HuffNode[6].weight=12;	//新结点权值为两个最小权值之和m1+m2
HuffNode[6].lchild=0;	//新结点n+i的左孩子为x1
HuffNode[6].rchild=3;	//新结点n+i的右孩子为x2

数据更新后如下表所示:

Huffman树构建数组

weight parent lchild rchild value
0 5 6 -1 -1 a
1 32 -1 -1 -1 b
2 18 -1 -1 -1 c
3 7 6 -1 -1 d
4 25 -1 -1 -1 e
5 13 -1 -1 -1 f
6 12 -1 0 3
7 0 -1 -1 -1
8 0 -1 -1 -1
9 0 -1 -1 -1
10 0 -1 -1 -1

(2)i=1时,j=0;j<7;找双亲为-1,权值最小的两个数:

x1=6 x2=5;				//x1、x2为两个最小权值结点的序号
m1=12 m2=13;				//m1、m2为两个最小权值结点的权值
HuffNode[5].parent=7;	//新结点n+i的左孩子为x1
HuffNode[6].parent=7;	//新结点n+i的左孩子为x2
HuffNode[7].weight=25;	//新结点权值为两个最小权值之和m1+m2
HuffNode[7].lchild=6;	//新结点n+i的左孩子为x1
HuffNode[7].rchild=5;	//新结点n+i的右孩子为x2

数据更新后如下表所示:

Huffman树构建数组

weight parent lchild rchild value
0 5 6 -1 -1 a
1 32 -1 -1 -1 b
2 18 -1 -1 -1 c
3 7 6 -1 -1 d
4 25 -1 -1 -1 e
5 13 7 -1 -1 f
6 12 7 0 3
7 25 -1 6 5
8 0 -1 -1 -1
9 0 -1 -1 -1
10 0 -1 -1 -1

依次类推。

猜你喜欢

转载自blog.csdn.net/weixin_43165059/article/details/86755324