树(三):哈夫曼树和哈夫曼编码

作为数据结构的课程笔记,以便查阅。如有出错的地方,还请多多指正!

注:C++忘得太厉害了。。算法先用C实现,等之后复习了再改成C++

目录

  • 基本概念
  • 定义
  • 构造Huffman树
  • 应用
    • 最佳判定树
    • Huffman编码

基本概念

  • 路径:从树中一个结点到另一个结点之间的分支构成这两个结点间的~
  • 路径长度:路径上的分支数
  • 树的路径长度:从树根到每一个结点的路径长度之和
  • 树的带权路径长度( w p l wpl wpl:Weighted Path Length):树中所有带权结点的路径长度之和
    W P L = ∑ k = 1 n w k l k WPL=\sum_{k=1}^n w_k l_k WPL=k=1nwklk

定义

  • 哈夫曼树(Huffman) / 最优二叉树 ——带权路径长度最短的树

树(三):哈夫曼树和哈夫曼编码_第1张图片

构造Huffman树

贪心算法:权值大的结点更靠近根

  • 根据给定的 n n n个权值 { w 1 , w 2 , … … w n } \{w_1,w_2,……w_n\} {w1,w2,wn},构造 n n n棵只有根结点的二叉树,根结点的权值分别为 w j w_j wj
  • 在森林中选取两棵根结点权值最小的二叉树作为左右子树,构造一棵新的二叉树,新二叉树根结点的权值为其左右孩子的权值之和
  • 在森林中删除这两棵二叉树,并将新二叉树加入森林中
  • 重复上面两步(重复 n − 1 n-1 n1次),直到森林中只含一棵二叉树为止,这棵树即哈夫曼树

因为要合并 n − 1 n-1 n1次,每次合并就会多出一个结点,因此有 n n n个叶结点的Huffman树共有 ( n − 1 ) + n = 2 n − 1 (n-1)+n=2n-1 (n1)+n=2n1个结点( n 0 = n , n 1 = 0 , n 2 = n − 1 n_0=n, n_1=0, n_2=n-1 n0=n,n1=0,n2=n1)

应用

最佳判定树

例:编制一个将百分制转换成五分制的程序
树(三):哈夫曼树和哈夫曼编码_第2张图片
如果用下面的判定树,则80%以上的学生要经过2次以上的比较
树(三):哈夫曼树和哈夫曼编码_第3张图片
为了使比较次数最少,将比例作为权值构建如下Huffman树:
树(三):哈夫曼树和哈夫曼编码_第4张图片
进而得到最佳判定算法:

树(三):哈夫曼树和哈夫曼编码_第5张图片

Huffman编码

在数据通信中涉及到数据编码问题
树(三):哈夫曼树和哈夫曼编码_第6张图片
若要设计不等长编码,则必须是任意字符的编码都不是另一个字符的编码的前缀,这种编码叫前缀编码

  • 思想:根据字符出现频率编码,使电文总长最短(即频率越大的字符编码得到的二进制字符串越短)

  • 编码:根据字符出现频率构造Huffman树,然后将树中结点引向其左孩子的分支标“0”,引向其右孩子的分支标“1”;每个字符的编码即为从根到每个叶子的路径上得到的0、1序列

树(三):哈夫曼树和哈夫曼编码_第7张图片

  • 译码:从Huffman树根开始,从待译码电文中逐位取码。若编码是“0”,则向左走;若编码是“1”,则向右走,一旦到达叶子结点,则译出一个字符;再重新从根出发,直到电文结束
    树(三):哈夫曼树和哈夫曼编码_第8张图片

  • 哈夫曼编码的正确性:

  1. 哈夫曼编码得出的编码肯定是前缀编码(数据仅存在于叶结点)
  2. 编码长度最短( W P L WPL WPL最短)
  • 下面是我用最小堆(最小堆的具体实现之后会发到专门的博客里)实现的算法,整体时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)
//用于最小堆中的比较大小
int Huffman_tree_larger(pBiTree_t t1, pBiTree_t t2)
{
	return (t1->data > t2->data);
}

//树中结点
pBiTree_t Build_huffman_tree(TreeElementType_t weight[], int size)
{
	Heap_t min_heap;
	pBiTree_t tree;

	Create_min_heap(&min_heap);

	//向最小堆中插入n个元素:T(n)=O(nlogn)
	for (int i = 0; i < size; ++i)
	{
		tree = (pBiTree_t)malloc(sizeof(BiTree_t));
		tree->data = weight[i];
		tree->L = NULL;
		tree->R = NULL;

		Min_heap_insert(&min_heap, tree, Huffman_tree_larger);
	}

	//删除2(n-1)次,插入n-1次:T(n)=O(nlogn)
	for (int i = 0; i < size - 1; ++i)
	{
		tree = (pBiTree_t)malloc(sizeof(BiTree_t));
		tree->L = Min_heap_delete(&min_heap, Huffman_tree_larger);
		tree->R = Min_heap_delete(&min_heap, Huffman_tree_larger);
		tree->data = tree->L->data + tree->R->data;
		Min_heap_insert(&min_heap, tree, Huffman_tree_larger);
	}

	return Min_heap_delete(&min_heap, Huffman_tree_larger);//返回哈夫曼树的根结点
}

你可能感兴趣的:(#,树)