Huffman霍夫曼压缩编码算法实现分析

哈夫曼编码Huffman方法于1952年问世,迄今为止仍经久不衰,广泛应用于各种数据压缩技术中,且仍不失为熵编码中的最佳编码方法,deflate等压缩算法也是结合了huffman算法的。

采用霍夫曼编码时有两个问题值得注意:

①霍夫曼码没有错误保护功能,在译码时,如果码串中没有错误,那么就能一个接一个地正确译出代码。但如果码串中有错误,哪仅是1位出现错误,不但这个码本身译错,更糟糕的是一错一大串,全乱了套,这种现象称为错误传播(error propagation)。计算机对这种错误也无能为力,说不出错在哪里,更谈不上去纠正它。

②霍夫曼码是可变长度码,因此很难随意查找或调用压缩文件中间的内容,然后再译码,这就需要在存储代码之前加以考虑。尽管如此,霍夫曼码还是得到广泛应用。

/*
霍夫曼编码模型:思想是压缩数据出现概率高的用短编码,出现概率低的用长编码,且每个字符编码都不一样。
压缩数据单个字符出现的概率抽象为叶子节点的权值,huffman树叶子节点到根节点的编码(是父节点左子节点那么填0,否则填1)
作为字符的唯一编码.

实现时候需要注意的规则:
1)最左的放置在左边,作为父节点的左节点。
2)每次都从没有设置父节点的所用节点中(叶子和分支节点一样对待),从数组小下标到大下标优先顺序遍历。
3)当前搜寻的次数i + n作为新生成的分支节点的数组下标。

实现的过程和具体算法思想:
两个数据结构:
一个是huffman树节点结构体,一个是从霍夫曼树叶子节点编码的结构体。

两个处理过程:
1)建立huffman树:
 基本思想是:对于没有设置父节点的节点集选出最小的两个,最小的放置在左边,次小的放置在右边
 设置好父节点和左右子节点关系,方便获得霍夫曼编码。

2) 从huffman树得到叶子节点的huffman编码:
 基本思想:对于建立好的Huffman树的每个叶子节点,由编码的数组的末端也是从叶子节点最底端,往上遍历
 如果是父节点的左节点那么用编码数组填1,如果是父节点的右节点那么编码数组填0,一直往上追溯到根节点。
*/
// 以下是实现的代码,在win32编译通过。
#include "stdafx.h"
#define MAXCODELEN 7
#define MAXWEIGHT 10000

struct tagHuffmanNode
{
	int m_nWeight;
	int m_nParent;
	int m_nLChild;
	int m_nRChild;
};

struct tagHuffmanCode
{
	int m_nWeight;
	int m_nStart;
	int m_nBit[MAXCODELEN];
};

void Huffman(int w[], int n, tagHuffmanNode ht[], tagHuffmanCode hc[])
{
	int nTotalCount = 2 * n - 1;
	// 初始化填充好ht的所有权值,包括叶子节点和分支节点
	for(int i = 0; i < nTotalCount; i++)
	{
		if( i < n)
		{
			ht[i].m_nWeight = w[i];
		}
		else
		{
			ht[i].m_nWeight = 0;
		}

		ht[i].m_nParent = 0;
		ht[i].m_nLChild = ht[i].m_nRChild = -1;
	}

	// 构造一颗huffman树,设置n-1个分支节点(非叶子),
	// 基本思想是:对于没有设置父节点的节点集选出最小的两个,最小的放置在左边,次小的放置在右边
	// 设置好父节点和左右子节点关系,方便获得霍夫曼编码
	int nCurMinWeight,nCurSecondMinWeight;
	int nCurLeftChild, nCurRightChild;
	for( int i = 0; i < n-1; i++)
	{
		nCurMinWeight = nCurSecondMinWeight = MAXWEIGHT;
		nCurLeftChild = nCurRightChild = 0;
		// 确定一个分支节点,需要对n + i个节点进行筛选
		for( int j = 0; j < n + i; j++)
		{
			if( ht[j].m_nWeight < nCurMinWeight  && ht[j].m_nParent == 0)
			{
				nCurSecondMinWeight = nCurMinWeight;
				nCurRightChild = nCurLeftChild;
				nCurMinWeight = ht[j].m_nWeight;
				nCurLeftChild = j;
			}
			else if(ht[j].m_nWeight < nCurSecondMinWeight  && ht[j].m_nParent == 0)
			{
				nCurSecondMinWeight = ht[j].m_nWeight;
				nCurRightChild = j;
			}
		}
		// 得到分支节点,设置节点关系
		ht[n + i].m_nLChild = nCurLeftChild;
		ht[n + i].m_nRChild = nCurRightChild;
		ht[n + i].m_nWeight =nCurMinWeight + nCurSecondMinWeight;
		ht[nCurLeftChild].m_nParent = n + i;
		ht[nCurRightChild].m_nParent = n + i;
	}
	// 测试用
	/*for(int i = 0; i < nTotalCount; i++)
	{
		printf("--------------------------------\n");
		printf("ht[%d].m_nWeight: %d\n", i, ht[i].m_nWeight);
		printf("ht[%d].m_nParent: %d\n", i, ht[i].m_nParent);
		printf("ht[%d].m_nLChild: %d\n", i, ht[i].m_nLChild);
		printf("ht[%d].m_nRChild: %d\n", i, ht[i].m_nRChild);
	}*/

	// 记录下来每个叶子节点的huffman编码
	// 基本思想:对于建立好的Huffman树的每个叶子节点,由编码的数组的末端也是从叶子节点最底端,往上遍历
	// 如果是父节点的左节点那么用编码数组填1,如果是父节点的右节点那么编码数组填0,一直往上追溯到根节点。
	for(int k = 0; k < n; k++)
	{
		hc[k].m_nWeight = ht[k].m_nWeight;
		hc[k].m_nStart = n - 1;// start等于最大的值
		int nChild = k;
		int nParent = ht[k].m_nParent;
		while(nParent != 0)
		{
			if(nChild == ht[nParent].m_nLChild)
			{
				hc[k].m_nBit[hc[k].m_nStart] = 0;
			}
			else if(nChild == ht[nParent].m_nRChild)
			{
				hc[k].m_nBit[hc[k].m_nStart] = 1;
			}

			hc[k].m_nStart--;
			nChild = nParent;
			nParent = ht[nChild].m_nParent;
		}
		
		// 因为递减了需要增加,得到正确的起始下标
		hc[k].m_nStart++;
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	int nDataNum = 7;
	int nWeigt[] = {4, 2, 6, 8, 3, 2, 1};
	const int nMaxLen = 2 * nDataNum - 1;
	tagHuffmanNode *ht = new tagHuffmanNode[nMaxLen];
	tagHuffmanCode *hc = new tagHuffmanCode[nDataNum];
	
	Huffman(nWeigt, nDataNum, ht, hc);

	for(int i = 0; i < 7; i++)
	{
		printf("index: %d, weight: %d, hc[%d].m_nBit: ", i, hc[i].m_nWeight, i);
		for(int j = hc[i].m_nStart; j < 7; j++)
		{
			printf("%d", hc[i].m_nBit[j]);
		}
		printf("\n");
	}
	delete []ht;
	delete []hc;
	while(1);
	return 0;
}



  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
由于霍夫曼编码压缩算法比较复杂,需要先构建霍夫曼树,再进行编码和解码等操作,因此需要较为复杂的代码实现。以下是一个简单的 C 语言实现,仅供参考。 ``` #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_TREE_HT 1000 /* 定义霍夫曼树节点 */ typedef struct huffman_node { char data; int freq; struct huffman_node *left, *right; } huffman_node; /* 定义霍夫曼编码表 */ typedef struct huffman_table { char data; char code[MAX_TREE_HT]; int code_len; } huffman_table; /* 定义霍夫曼编码树 */ typedef struct huffman_tree { huffman_node *root; } huffman_tree; /* 创建霍夫曼树节点 */ huffman_node* create_node(char data, int freq) { huffman_node* node = (huffman_node*)malloc(sizeof(huffman_node)); node->data = data; node->freq = freq; node->left = NULL; node->right = NULL; return node; } /* 创建霍夫曼树 */ huffman_tree* create_tree(char *data, int *freq, int size) { huffman_tree* tree = (huffman_tree*)malloc(sizeof(huffman_tree)); huffman_node *node, *left, *right; int i, j, min1, min2; huffman_node* nodes[size]; for (i = 0; i < size; i++) { nodes[i] = create_node(data[i], freq[i]); } for (i = 0; i < size - 1; i++) { min1 = min2 = 1000000; left = right = NULL; for (j = 0; j < size - i; j++) { if (nodes[j]->freq < min1) { min2 = min1; right = left; min1 = nodes[j]->freq; left = nodes[j]; } else if (nodes[j]->freq < min2) { min2 = nodes[j]->freq; right = nodes[j]; } } node = create_node('\0', min1 + min2); node->left = left; node->right = right; nodes[size - i - 2] = node; } tree->root = nodes[0]; return tree; } /* 生成霍夫曼编码表 */ void generate_table(huffman_node *node, huffman_table *table, char *code, int len) { if (node == NULL) { return; } if (node->left == NULL && node->right == NULL) { int i; for (i = 0; i < len; i++) { table[node->data].code[i] = code[i]; } table[node->data].code_len = len; } code[len] = '0'; generate_table(node->left, table, code, len + 1); code[len] = '1'; generate_table(node->right, table, code, len + 1); } /* 压缩数据 */ void compress_data(char *data, int *freq, int size, char *compressed_data, int *compressed_size) { huffman_table table[256]; char code[MAX_TREE_HT]; int i, j, len, k, l; huffman_tree *tree = create_tree(data, freq, size); generate_table(tree->root, table, code, 0); len = strlen(data); k = l = 0; for (i = 0; i < len; i++) { for (j = 0; j < table[data[i]].code_len; j++) { if (table[data[i]].code[j] == '0') { compressed_data[k] &= ~(1 << (7 - l)); } else { compressed_data[k] |= 1 << (7 - l); } l++; if (l == 8) { l = 0; k++; } } } if (l != 0) { k++; } *compressed_size = k; } /* 解压数据 */ void decompress_data(char *compressed_data, int compressed_size, char *decompressed_data, int *decompressed_size) { huffman_node *node; int i, j, k, l, bit; huffman_tree *tree = (huffman_tree*)malloc(sizeof(huffman_tree)); node = create_node('\0', 0); tree->root = node; k = l = 0; for (i = 0; i < compressed_size; i++) { for (j = 7; j >= 0; j--) { bit = (compressed_data[i] >> j) & 1; if (bit == 0) { if (node->left == NULL) { node->left = create_node('\0', 0); } node = node->left; } else { if (node->right == NULL) { node->right = create_node('\0', 0); } node = node->right; } if (node->left == NULL && node->right == NULL) { decompressed_data[k++] = node->data; node = tree->root; } } } *decompressed_size = k; } /* 测试 */ int main() { char data[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g'}; int freq[] = {5, 9, 12, 13, 16, 45, 50}; char compressed_data[1000]; char decompressed_data[1000]; int compressed_size, decompressed_size; compress_data(data, freq, 7, compressed_data, &compressed_size); decompress_data(compressed_data, compressed_size, decompressed_data, &decompressed_size); printf("Original data: %s\n", data); printf("Compressed data: "); for (int i = 0; i < compressed_size; i++) { printf("%02x ", compressed_data[i]); } printf("\nDecompressed data: %s\n", decompressed_data); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值