哈夫曼压缩算法C语言实现——步骤,详细注释源码

哈夫曼压缩算法的详细实现步骤:

1、定义哈夫曼树节点,用结构体。

2、利用C语言文件读写,统计字符个数。

3、根据字符个数创建哈夫曼树(不懂haffman数据结构的自己查下资料,我这里就不再重复了)

4、根据哈夫曼树为每个出现的字符编码

5、压缩:这里涉及到位操作,用char8位来储存字符编码(压缩的关键)。

6、解压缩:读压缩文件,在char中从左到右按位遍历哈夫曼树

(再不懂的看源码,注释够意思了。)

#include<stdio.h>
#include<stdlib.h>
#include<string.h> 
//定义一个哈夫曼节点,它是个结构体
struct haffNode{
	int weight;//权重,就是这个字符出现的个数;如果这个节点是个父节点的话 ,就是两个子节点权重的和 
	char data;//这个用来存字符本身,比如字符是'c',data='c'; 
	struct haffNode *leftChild=NULL,*rightChild=NULL;//定义左右子节点指针 
}; 
char code[256][50];//用二维数组来储存字符的哈夫曼编码,其中第一维的下标表示的是这个字符的ASCII码  
haffNode left[50];//用来储存所有的左子节点 
haffNode right[50];//用来储存所有的右子节点 
unsigned char saveChar = 0; //用来保存二进制文件,因为char类型是1个字节,所以每8位储存一次 ,而且用unsigned无符号型,避免符号位干扰 
unsigned char slidChar;//定义一个字符备用,以应对可能需要的操作 
//排序函数,第一个参数是哈夫曼的节点数组,第二个是数组的长度,这里用冒泡排序 
void sort(haffNode* node,int length){
	int i,j; 
	haffNode t;
	for(i=0; i<length-1; i++){
		for(j=i+1; j<length-i-1;j++){
			if(node[j].weight < node[j+1].weight){
				t = node[j];
				node[j] = node[j+1];
				node[j+1] = t;
			}
		}
	} 
} 
//构建哈夫曼树 
void creatHaffman(haffNode *node,int length){
	if(length==1){
	return; //如果数组长度为1,则结束递归,下面的就不再执行 
	}
	sort(node,length);	//将node数组按照weight从大到小排序 
	haffNode parent;//生成父节点,因为我们的数组从大到小排序过了,所以数组最后面的就是最小的节点 
	left[length] = node[length-2],right[length]=node[length-1];//定义子字节,用来存数组最后的两个节点 
	parent.weight = left[length].weight + right[length].weight;//父节点的权重等于两个子节点的 权重
	//储存两个子节点,因为parent.leftChild是指针类型,所以赋值的时候要加& 
	parent.leftChild= &left[length];
	parent.rightChild = &right[length]; 
	//将数组最后两个子节点剔除,换成父节点,然后递归创建接下来的部分 
	node[length-2] = parent;
	creatHaffman(node,length-1); 
}
//计算字符的哈夫曼编码 ,第一个参数是哈夫曼树根节点,第二个参数储存编码的字符数组,第三个参数是字符数组的长度,从0开始 
void coding(haffNode *node,char *keepCode,int length){
	//如果节点没有子节点,就说明它是叶节点,将编码存在code数组里 
	if(node->leftChild == NULL || node->rightChild == NULL){
		keepCode[length] ='\0';//给编码一个终止符,形成一个完整的字符串,方便拷贝,以防拷贝到之前的编码。 
		strcpy(code[node->data-0],keepCode);//调用strcpy函数拷贝字符串,其中code的下标用节点的字符(data)-0得到 
		return; 
	}
	keepCode[length] = '0';
	coding(node->leftChild,keepCode,length+1);
	keepCode[length] = '1';
	coding(node->rightChild,keepCode,length+1);
} 
//解压缩  
haffNode* unzip(haffNode *node,int flag){
	if(flag == 0)
	return node->leftChild;
	if(flag == 1)
	return node->rightChild;
} 
int main(){
    int count[128]={0};//用来统计字符个数,一开始清零,其中它的下标号表示这个字符的ASCII码
    char keepCode[50];//用于在生成编码的时候 临时储存编码 ,真正储存编码的地方看代码最上面的code[][]数组 
    char reder;//用来存文件中的字符 
    int fileLength=0;//用来计算原文长度 
    int zipLength=0;//用来计算压缩文长度 
    int i; 
    int num=0;//用来计算出现的字符的个数 和其它一些计数功能 
    haffNode node[100];//用来储存哈夫曼节点,这里我申请100个空间事实上不需要那么大,大概需要26+26+20(26个英文字母大小写+标点符号) 
	FILE *fpr = fopen("E:\\input.txt","r");//读入文件,其中input.txt的路径你要自己设置,可以自己建立一个文本,写一些英文进去 
	FILE *fpw = fopen("E:\\output1","wb");//写入文件,wb是写入二进制文件,路径设置随意,但是要符合格式,写入的时候文件自动生成。
	FILE *fpr1= fopen("E:\\output1","rb");;//用于解压缩时读入文件 ,rb是读入二进制文件 
	FILE *fpw1 = fopen("E:\\output3.txt","w"); //用于解压缩时写入文件 
	//解压需要用的 
	char op;
	haffNode *z;
	//读取文件 
	while((reder=fgetc(fpr))!=EOF){//一个一个地读入字符 
		fileLength ++;//每读进一个字原文长度+1 
		count[reder-0]++;//reder-0可以得到字符的ASCII码,然后累加统计 
	} 
	//循环数组,因为ASCII表中有255个字符,所以数组中有些字符是完全没有出现过的,我们要将出现过的存在charNode数组里。 
	for(i=0; i<128; i++){
		if(count[i]!=0){
			node[num].data=i;//之前说过,下标就是出现的字符的ASCII码
			node[num].weight=count[i];//count[i]存的就是字符出现的次数
			num++;//计数加1 
		}
	}
	//构建哈夫曼树
	creatHaffman(node,num); 
	//计算哈夫曼编码
	coding(&node[0],keepCode,0); 
	//根据哈夫曼编码把原来的文本压缩储存 
	//读取文件 
	num=0;//计数 
	fseek(fpr,0L,0);//因为上面已经读过文件了,fpr指针已经向下移动,所以这边使用 fseek函数将指针复原到离0(文件起始位置)0L(第0个字节)处 
	while((reder=fgetc(fpr))!=EOF){ //一个一个地读入字符 
		for(i=0; i<strlen(code[reder-0]); i++){
			saveChar |= code[reder-0][i]-'0';//让saveChar和编码中的每一位进行或操作,用字符的'1'-'0',就可得到0000 0001.     
			num++;
			if(num == 8){
				fwrite(&saveChar,sizeof(char),1,fpw);//每8位写入一次文件
				zipLength++; 
				saveChar = 0;//重新置0 
				num=0;
			} 
			else{
				saveChar = saveChar << 1	; //每做完一步,向左移一位  
			}
		}
	} 
	//如果最后剩余的编码不到8位,将其移到最左端
	if(num != 8){
		saveChar = saveChar<<(8-num);//移到最左端 
		fwrite(&saveChar,sizeof(char),1,fpw);
		zipLength++;
	} 
	fclose(fpr);
	fclose(fpw);
	//根据哈夫曼编码解压缩,主要思想是根据编码遍历哈夫曼树 
	num=0;//计算解压缩后的文件长度 
	z = &node[0];
	while(fread(&reder,sizeof(char),1,fpr1)){
		//如果解压缩的长度等于原文长度,停止解压缩。为什么多这一个条件,因为在编码压缩的时候可能出现剩余的编码不足8位的情况,不足8位不成8位后,后面的补位可能造成干扰 
		if(fileLength == num){
			break;
		} 
		op = 128;//1000 0000
		for(i=0; i< 8; i++){
		slidChar = reder & op;
		reder = reder << 1;
		slidChar = slidChar >>7;
		z = unzip(z,slidChar-0);
		if(z->leftChild == NULL || z->rightChild == NULL){
			fprintf(fpw1,"%c",z->data);
			num++;//每写进一个字符+1 
			z = &node[0];
		}
	}
	}
	fclose(fpr1);
	fclose(fpw1);
	//计算压缩率	
	printf("原文件:%dK\n",fileLength/1024+1);
	printf("压缩完成!请查看output1:%dK\n",zipLength/1024+1); 
	printf("解压缩完成!请查看output3.txt:\%dK\n",fileLength/1024+1); 
	printf("压缩率:%.2f%%\n",(float)(fileLength-zipLength)/fileLength*100);	
    return 0;
    } 
    




  • 37
    点赞
  • 203
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
以下是C语言实现哈夫曼压缩算法的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_TREE_HT 100 struct MinHeapNode { char data; unsigned freq; struct MinHeapNode *left, *right; }; struct MinHeap { unsigned size; unsigned capacity; struct MinHeapNode **array; }; struct MinHeapNode *newNode(char data, unsigned freq) { struct MinHeapNode *temp = (struct MinHeapNode *)malloc(sizeof(struct MinHeapNode)); temp->left = temp->right = NULL; temp->data = data; temp->freq = freq; return temp; } struct MinHeap *createMinHeap(unsigned capacity) { struct MinHeap *minHeap = (struct MinHeap *)malloc(sizeof(struct MinHeap)); minHeap->size = 0; minHeap->capacity = capacity; minHeap->array = (struct MinHeapNode **)malloc(minHeap->capacity * sizeof(struct MinHeapNode *)); return minHeap; } void swapMinHeapNode(struct MinHeapNode **a, struct MinHeapNode **b) { struct MinHeapNode *t = *a; *a = *b; *b = t; } void minHeapify(struct MinHeap *minHeap, int idx) { int smallest = idx; int left = 2 * idx + 1; int right = 2 * idx + 2; if (left < minHeap->size && minHeap->array[left]->freq < minHeap->array[smallest]->freq) smallest = left; if (right < minHeap->size && minHeap->array[right]->freq < minHeap->array[smallest]->freq) smallest = right; if (smallest != idx) { swapMinHeapNode(&minHeap->array[smallest], &minHeap->array[idx]); minHeapify(minHeap, smallest); } } int isSizeOne(struct MinHeap *minHeap) { return (minHeap->size == 1); } struct MinHeapNode *extractMin(struct MinHeap *minHeap) { struct MinHeapNode *temp = minHeap->array[0]; minHeap->array[0] = minHeap->array[minHeap->size - 1]; --minHeap->size; minHeapify(minHeap, 0); return temp; } void insertMinHeap(struct MinHeap *minHeap, struct MinHeapNode *minHeapNode) { ++minHeap->size; int i = minHeap->size - 1; while (i && minHeapNode->freq < minHeap->array[(i - 1) / 2]->freq) { minHeap->array[i] = minHeap->array[(i - 1) / 2]; i = (i - 1) / 2; } minHeap->array[i] = minHeapNode; } void buildMinHeap(struct MinHeap *minHeap) { int n = minHeap->size - 1; int i; for (i = (n - 1) / 2; i >= 0; --i) minHeapify(minHeap, i); } void printArr(int arr[], int n) { int i; for (i = 0; i < n; ++i) printf("%d", arr[i]); printf("\n"); } int isLeaf(struct MinHeapNode *root) { return !(root->left) && !(root->right); } struct MinHeap *createAndBuildMinHeap(char data[], int freq[], int size) { struct MinHeap *minHeap = createMinHeap(size); for (int i = 0; i < size; ++i) minHeap->array[i] = newNode(data[i], freq[i]); minHeap->size = size; buildMinHeap(minHeap); return minHeap; } struct MinHeapNode *buildHuffmanTree(char data[], int freq[], int size) { struct MinHeapNode *left, *right, *top; struct MinHeap *minHeap = createAndBuildMinHeap(data, freq, size); while (!isSizeOne(minHeap)) { left = extractMin(minHeap); right = extractMin(minHeap); top = newNode('$', left->freq + right->freq); top->left = left; top->right = right; insertMinHeap(minHeap, top); } return extractMin(minHeap); } void printCodes(struct MinHeapNode *root, int arr[], int top) { if (root->left) { arr[top] = 0; printCodes(root->left, arr, top + 1); } if (root->right) { arr[top] = 1; printCodes(root->right, arr, top + 1); } if (isLeaf(root)) { printf("%c: ", root->data); printArr(arr, top); } } void HuffmanCodes(char data[], int freq[], int size) { struct MinHeapNode *root = buildHuffmanTree(data, freq, size); int arr[MAX_TREE_HT], top = 0; printCodes(root, arr, top); } int main() { char arr[] = {'a', 'b', 'c', 'd', 'e', 'f'}; int freq[] = {5, 9, 12, 13, 16, 45}; int size = sizeof(arr) / sizeof(arr[0]); HuffmanCodes(arr, freq, size); return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值