哈夫曼树及其哈夫曼编码(数据结构C语言)

哈夫曼树及其哈夫曼编码

输入5种字符以及他们的权值:a:10, b:5, c:20, d:8,e:15
第一步:构建哈夫曼树
在这里插入图片描述
第二步:为哈夫曼树的每一条边编码(左0右1),图中没有标
在这里插入图片描述
代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define N 5			//带权值的叶子节点数
#define M 2*N-1		//n个叶子节点构造的哈夫曼树有2n-1个结点
#define MAX 10000
typedef char TElemType;

typedef struct{
	unsigned int weight;     //权值只能是正数
	unsigned int parent,lchild,rchild;
}HTNode;

typedef HTNode HuffmanTree[M+1];		//动态分配数组存储哈夫曼树,0号单元不适用
typedef char *HuffmanCode[N+1];			//动态分配数组存储哈夫曼编码



//在HT[1...k]里选择parent为0的且权值最小的2结点,其序号分别为s1,s2,
void Select(HuffmanTree HT,int k,int &s1,int &s2){
	
	unsigned int tmp = MAX ,temp = 0;
	for(int i=1;i<=k;i++){
		if(!HT[i].parent ){  //双亲为0
			//找出最小值
			if(tmp>HT[i].weight ){
				tmp = HT[i].weight ;     //tmp最后为最小的weight
				temp = i;
			}
		}
	}
	s1 = temp;
	tmp = MAX;
	temp = 0;
	for(int i = 1;i<=k;i++){
		if((!HT[i].parent )&& i!=s1){
			//找出第二个小值
			if(tmp > HT[i].weight ){
				tmp = HT[i].weight ;
				temp = i;
			}
		}
	}
	s2 = temp;
}

void CreateHuffmanCoding (HuffmanTree &HT,HuffmanCode &HC,int *w,int n){
	//存放n个字符的权值(均>0),构造哈夫曼树HT,并求出n个字符的哈夫曼树HC
	int i;
	if(n<=0)   //空树
		return ;
	//对树赋初值
	for(i = 1;i<=n;i++){    //将哈夫曼表的双亲,左右子树置0
		HT[i].weight = w[i];
		HT[i].lchild = 0;
		HT[i].parent = 0;
		HT[i].rchild = 0;

	}
	for(i = n+1;i<=M;i++){
		HT[i].weight = 0;
		HT[i].lchild = 0;
		HT[i].parent = 0;
		HT[i].rchild = 0;
	}
	//创建哈夫曼树
	for(i=n+1;i<=M;i++){
		int s1,s2;
		Select(HT,i-1,s1,s2);
		HT[s1].parent  = i;
		HT[s2].parent = i;
		HT[i].lchild = s1;
		HT[i].rchild = s2;
		HT[i].weight = HT[s1].weight + HT[s2].weight ;   //值i的权值
	}
	
	//为每个字符求解哈夫曼编码,从叶子到根逆向求解每个字符的哈夫曼编码
	char *ch = (char *)malloc (n*sizeof (char));
	ch[n-1]='\0';			//编码结束符
	int start ,c,f;
	for(int i = 1;i<=N;i++){     //逐个字符求哈夫曼编码
		start = N-1;			//编码结束的位置
		for(c=i,f=HT[i].parent ;f!=0;c=f,f=HT[f].parent ){     //从叶子到根 的逆向求编码
			if(HT[f].lchild == c){
				ch[--start] = '0';
			}else{
				ch[--start] = '1';
			}
			
		}
		//每次ch的后N-start个位置有编码存在
		HC[i]= (char *)malloc((N-start)*sizeof (char));
		//将ch的后N-start个元素赋值给H[i] 指向的字符串
		strcpy (HC[i],&ch[start]);
	}
	

} 
//打印哈夫曼树
void prinfHuffmanTree(HuffmanTree HT,char ch[]){
	printf("data\tweight\tparent\tlchild\trchild\n");
	for(int i = 1;i<=M;i++){
		if(i>N){
			printf(" -\t%d\t%d\t%d\t%d\n",HT [i].weight ,HT[i].parent ,HT[i].lchild ,HT[i].rchild );
		}else{
			printf(" %c\t%d\t%d\t%d\t%d\n",ch[i], HT [i].weight ,HT[i].parent ,HT[i].lchild ,HT[i].rchild );
		}

	}
}
//输出哈夫曼编码
void printHuffmanCoding(HuffmanCode HC,char ch[]){
	for(int i = 1; i<=N;i++){
		printf("%c:%s\n", ch[i],HC[i]);
	}
	printf("\n");
}

int main(){
	
	HuffmanTree HT;
	HuffmanCode HC;
	TElemType ch[N+1];
	int w[N+1];
	printf("\n********哈夫曼树及哈夫曼编码的存储表示:*********\n");
	printf("请输入%d 个字符以及字符对应的权值:\n",N);
	for(int i = 1;i<=N;i++){
		scanf("%c%d",&ch[i],&w[i]);
		getchar();		//去掉换行符
	}
	
	CreateHuffmanCoding (HT,HC,w,N);			//创建哈夫曼树
	printf("\n********哈夫曼树如下:*********\n");
	prinfHuffmanTree(HT,ch);
	printf("\n********哈夫曼编码:*********\n");
	printHuffmanCoding(HC,ch);

	return 0;
}

运行截图:
在这里插入图片描述

  • 2
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
好的,这是一个非常有趣的问题。实现一个基于Huffman树和Huffman编码的无损数据压缩程序,可以分为以下几个步骤: 1. 统计每个字符出现的频率,构建Huffman树 首先,需要对待压缩的数据进行遍历,统计每个字符出现的频率。然后,使用这些频率构建Huffman树。这个过程可以使用堆来实现,具体实现可以参考哈夫曼编码的构造算法。 2. 生成Huffman编码表 构建好Huffman树之后,可以根据Huffman树的特性,生成每个字符对应的Huffman编码。具体实现可以使用递归的方式,在遍历Huffman树的过程中生成Huffman编码表。 3. 将数据进行压缩 有了Huffman编码表之后,就可以对待压缩的数据进行压缩了。具体实现可以将数据中的每个字符替换成对应的Huffman编码,然后将所有的编码拼接在一起,形成一个二进制字符串。 4. 将压缩后的数据写入文件 最后,将压缩后的二进制字符串写入文件中即可。 这里提供一份参考代码,仅供参考: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_CHAR_NUM 256 #define MAX_BIT_NUM 1000000 typedef struct TreeNode { unsigned char data; // 存储字符 int freq; // 存储字符出现的频率 struct TreeNode *left; struct TreeNode *right; } TreeNode; typedef struct Heap { int size; TreeNode *data[MAX_CHAR_NUM*2]; } Heap; typedef struct HuffmanCode { unsigned char data; char code[MAX_BIT_NUM]; } HuffmanCode; void swap(TreeNode **a, TreeNode **b) { TreeNode *temp = *a; *a = *b; *b = temp; } void minHeapify(Heap *heap, int index) { int left = index * 2, right = index * 2 + 1, smallest = index; if (left <= heap->size && heap->data[left]->freq < heap->data[smallest]->freq) smallest = left; if (right <= heap->size && heap->data[right]->freq < heap->data[smallest]->freq) smallest = right; if (smallest != index) { swap(&heap->data[index], &heap->data[smallest]); minHeapify(heap, smallest); } } void buildMinHeap(Heap *heap) { int i; for (i = heap->size / 2; i >= 1; i--) minHeapify(heap, i); } TreeNode *createTreeNode(unsigned char data, int freq) { TreeNode *newNode = (TreeNode*) malloc(sizeof(TreeNode)); newNode->data = data; newNode->freq = freq; newNode->left = NULL; newNode->right = NULL; return newNode; } Heap *createHeap() { Heap *newHeap = (Heap*) malloc(sizeof(Heap)); newHeap->size = 0; return newHeap; } void insertHeap(Heap *heap, TreeNode *node) { heap->size++; heap->data[heap->size] = node; int i = heap->size; while (i > 1 && heap->data[i]->freq < heap->data[i/2]->freq) { swap(&heap->data[i], &heap->data[i/2]); i = i / 2; } } TreeNode *deleteMin(Heap *heap) { TreeNode *minNode = heap->data[1]; heap->data[1] = heap->data[heap->size]; heap->size--; minHeapify(heap, 1); return minNode; } TreeNode *buildHuffmanTree(int freq[]) { int i; Heap *heap = createHeap(); for (i = 0; i < MAX_CHAR_NUM; i++) if (freq[i] > 0) insertHeap(heap, createTreeNode(i, freq[i])); buildMinHeap(heap); while (heap->size > 1) { TreeNode *left = deleteMin(heap); TreeNode *right = deleteMin(heap); TreeNode *parent = createTreeNode('$', left->freq + right->freq); parent->left = left; parent->right = right; insertHeap(heap, parent); } return deleteMin(heap); } void generateHuffmanCode(TreeNode *root, char code[], int depth, HuffmanCode huffmanCodes[]) { if (root->left == NULL && root->right == NULL) { huffmanCodes[root->data].data = root->data; strcpy(huffmanCodes[root->data].code, code); return; } code[depth] = '0'; generateHuffmanCode(root->left, code, depth+1, huffmanCodes); code[depth] = '1'; generateHuffmanCode(root->right, code, depth+1, huffmanCodes); } void compressFile(char *inputFile, char *outputFile, HuffmanCode huffmanCodes[]) { FILE *fin = fopen(inputFile, "rb"); FILE *fout = fopen(outputFile, "wb"); int freq[MAX_CHAR_NUM], i; memset(freq, 0, sizeof(freq)); unsigned char c; while (fread(&c, 1, 1, fin) > 0) freq[c]++; fseek(fin, 0, SEEK_SET); TreeNode *root = buildHuffmanTree(freq); char code[MAX_BIT_NUM]; generateHuffmanCode(root, code, 0, huffmanCodes); int bitCount = 0; char buffer = 0; while (fread(&c, 1, 1, fin) > 0) { for (i = 0; i < strlen(huffmanCodes[c].code); i++) { buffer = buffer << 1; if (huffmanCodes[c].code[i] == '1') buffer |= 1; bitCount++; if (bitCount == 8) { fwrite(&buffer, 1, 1, fout); bitCount = 0; buffer = 0; } } } if (bitCount > 0) { buffer = buffer << (8 - bitCount); fwrite(&buffer, 1, 1, fout); } fclose(fin); fclose(fout); } void decompressFile(char *inputFile, char *outputFile, TreeNode *root) { FILE *fin = fopen(inputFile, "rb"); FILE *fout = fopen(outputFile, "wb"); unsigned char c; TreeNode *p = root; while (fread(&c, 1, 1, fin) > 0) { int i; for (i = 7; i >= 0; i--) { if ((c & (1 << i)) == 0) p = p->left; else p = p->right; if (p->left == NULL && p->right == NULL) { fwrite(&p->data, 1, 1, fout); p = root; } } } fclose(fin); fclose(fout); } int main() { char inputFile[] = "input.txt"; char compressedFile[] = "compressed.bin"; char decompressedFile[] = "decompressed.txt"; HuffmanCode huffmanCodes[MAX_CHAR_NUM]; memset(huffmanCodes, 0, sizeof(huffmanCodes)); compressFile(inputFile, compressedFile, huffmanCodes); TreeNode *root = buildHuffmanTree(NULL); decompressFile(compressedFile, decompressedFile, root); return 0; } ``` 这份代码使用了堆来构建Huffman树,使用了递归的方式来生成Huffman编码表,并且实现了对文件进行压缩和解压缩的功能。需要注意的是,对于一个字符集大小为N的情况,Huffman树的构建时间复杂度为O(NlogN),压缩和解压缩的时间复杂度也为O(NlogN)。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YJY@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值