基于C语言利用哈夫曼树实现文件压缩的问题

一、哈夫曼树

具有n个权值的n个叶子结点,构造出一个二叉树,使得该树的带权路径长度(WPL)最小,则称此二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。

注意:哈夫曼树是带权路径长度最短的树,且权值越大的叶子结点离根结点越近。

二、哈夫曼编码

        哈夫曼编码是一种编码方式,又称“霍夫曼编码”,其是可变字长的编码(VCL)的一种,是由霍夫曼于1952年提出的一种编码方式,有时被称为最佳编码,一般称为Huffman编码

那么我们为什么要使用哈夫曼编码进行压缩?

首先原因在于,传统压缩方式中,字符的编码是以等字长的方式进行压缩,相比于哈夫曼编码的可变字长而言压缩率会降低。其次,传统方式中可能会出现某些字符的编码相同,存在二义性,相比于哈夫曼编码的唯一性。可以大大提高正确性。

2.1 如何得到哈夫曼编码

通过已经构造的哈夫曼树,结点左分支记为二进制数“0”,结点右分支记为二进制数“1”。从根结点向下到该叶子结点路途经过的数字拼接成为的编码即为该字符的哈夫曼编码。现通过以下图例进行演示。

此时各字符对应的哈夫曼编码可表示为:

A:00        B:01        C:1

三、哈夫曼树实现文件压缩基本步骤

3.1 基本实现流程

3.2 使用文件输入流读取需压缩的文件,并判断该文件是否存在

存在后执行接下来的步骤,不存在提示用户,效果图如下

3.3 读取,统计字符

用户输入正确文件路径且存在时使用文件输入流读取文件,将文件放于一个字符数组中(注意字符数组开辟空间应足够大)。统计出现的字符及其出现的次数。

char ch;
		while(feof(fp) == 0){
			fscanf(fp,"%c",&ch);
			str[ch]++;
		}
		printf("\n");
		for(i = 0; i < 1024; i++){
			if(str[i] != 0){
				count[n++] = i;
			}
		}

3.4 构造哈夫曼树

注:由于后续会使用此哈夫曼编码,为了方便获取,这里将求出的哈夫曼编码放置于一个二维数组中。

 CreateHT(ht,node);
		char strarr[256][256];
		int aa,bb;
		for(aa=0; aa<256;aa++)
			for(bb=0;bb<256;bb++)
				strarr[aa][bb]=-1; 

		for(k = 0;k < node;k++){
			huffmanCode(ht,k,strarr[k]);
		}
void CreateHT(HTNode *ht, int n){
	int i, j, lnode, rnode n1, n2;
	for(i = n; i < 2*n-1; i++){
		lnode = 65432; rnode = lnode;
		n1 = 0; n2 = 0;
		for(j = 0; j < i; j++){
			if(ht[j].parent == -1){
				if(ht[j].weight <lnode){
					rnode = lnode;
					n2 =n1;
					lnode = ht[j].weight;
					n1 = j;
				} else if(ht[j].weight < rnode){
					rnode = ht[j].weight;
					n2 = j;
				}
			}
		}
		ht[n1].parent = i;
		ht[n2].parent = i;

		ht[i].weight = lnode+rnode;
		ht[i].parent = -1;
		ht[i].lchild = n1;
		ht[i].rchild = n2;
	}
}

3.5 根据构造哈夫曼树得到哈夫曼编码

此时我们使用3.3中统计的字符作为叶子结点,其出现次数作为权值,构造哈夫曼树求出哈夫曼编码。下面以此段英文为例,得到每个字符对应哈夫曼编码。

You can take away our money,house,car,or even our clothes and we can surive.But if our health was taken away,we would surely die.That is why we always try to eat in a healthy way and excise regularly..

3.6 替换字符为其哈夫曼编码

用户输入压缩至的文件路径,将替换好的0 1数字字符数组写入该文件。由于C语言没有基于字符串的操做,所以我们替换时只能通过对字符数组进行操作,涉及到了被替换字符串长度替换字符串长度的问题,以及字符数组的长度是否充足。由于此次替换被替换字符串只是一个字符,且字符数组开辟空间足够大。所以只考虑替换字符串长度,即字符对应哈夫曼编码的长度,在这里使用一个方法来进行替换。

void replace(char oldstr[], char key[], char rep[]){
	int oldstrLen, lengthOfKey, lengthOfRep, i, j , flag;
	char tmp[1000];
	oldstrLen = strlen(oldstr);
	lengthOfKey = strlen(key);
	lengthOfRep = strlen(rep);

	for( i = 0; i <= oldstrLen - lengthOfKey; i++){
		flag = 1;
		for(j = 0; j < lengthOfKey; j++){
			if(oldstr[i + j] != key[j]){
				flag = 0;
				break;
			}
		}
		if(flag){
			strcpy(tmp, oldstr);
			strcpy(&tmp[i], rep);
			strcpy(&tmp[i + lengthOfRep], &oldstr[i + lengthOfKey]);
			strcpy(oldstr, tmp);
			i += lengthOfRep - 1;
			oldstrLen = strlen(oldstr);
		}
	}
}

替换完成后的字符数组使用输出流写入用户输入文件路径。

3.7 进制转换

将文件中每八位二进制数转换为十进制数,再转换为其对应的ASCII码值,最后不足八位的二进制数以0补齐,实现文件压缩。

3.7.1 进制转换方法

int retTen(char str[]){
	int ten = 0;
	int t = 0;
	for(t = 0;t < strlen(str);t++){
		ten += (str[t]-'0') * pow(2,strlen(str) - t - 1);
	}
	return ten;
}

3.7.2 实现效果

3.8 计算压缩率

最后为验证是否实现压缩,可通过计算文件压缩率来验证。其中,压缩率=原文件字节大小/压缩后文件大小。

int oldFileSize=getFileSize(path);
int newFileSize=getFileSize(Gopath);

printf("\n");
printf("压缩成功!,文件已压缩至%s\n",Gopath);
printf("该文件压缩率为:%.2lf\n",(double)newFileSize/(double)oldFileSize);

注:注意计算压缩率时的类型转换

到此这篇关于基于C语言利用哈夫曼树实现文件压缩的文章就介绍到这了,更多相关C语言实现文件压缩内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C语言进阶教程之循环语句缺陷详析

    目录 前言 1 循环语句的三要素 2 使用不同循环语句实现六种排列组合 2.1 第一种排列(ABC) 2.2 第二种排列(ACB) 2.3 第三种排列(BCA) 2.4 第四种排列(CBA) 2.5 第五种排列(BAC) 2.6 第六种排列(CAB) 3 什么时候用for循环语句 4 什么时候用while循环语句 5 什么时候用do-while循环语句 6 其他情况 7 总结 前言 你是否也有过下面的体会? 为什么刚开始学习C语言时很喜欢用for循环语句,但逐渐发现有经验的工程师都在用while

  • 关于C语言中参数的传值问题

    1. 考题一:程序代码如下: 复制代码 代码如下: void Exchg1(int x, int y) { int tmp; tmp=x; x=y; y=tmp; printf("x=%d,y=%d/n",x,y) } void main() { int a=4,b=6; Exchg1 (a,b) ; printf("a=%d,b=%d/n",a,b) } 输出的结果: x=____, y=____ a=____, b=____ 问下划线的部分应是什么,请完成. 2

  • C语言:传值与传址交换整数

    目录 传值调用 传址调用 总结 传值调用 要想交换两个整数的值,我们可以自定义一个函数,来实现这个过程.具体代码如下: #include <stdio.h> void Swap1(int x, int y) { int z = 0; z = x; x = y; y = z; } int main() { int a = 10; int b = 20; pirntf("交换前:a=%d b=%d", a, b); Swap1(a, b); pirntf("交换前:a

  • C语言中传值与传指针的介绍与区别

    前言 我们可能听过C语言中的传值和传指针,在其他语言中,也有传引用一说,那么他们到底有什么区别呢?如果你还不能准确地分辨,就该好好了解一下了. 传值 我们在初学C语言的时候就被老师教过,下面的方式是无法交换a和b的值的: #include<stdio.h> void swap(int a,int b) { int temp = a; a = b; b = temp; printf("swap a = %d,b = %d\n",a,b); } int main(void) {

  • C语言版的三子棋游戏

    本文实例为大家分享了C语言版三子棋游戏的具体代码,供大家参考,具体内容如下 一.游戏说明 三子棋相信大家都不陌生,小时候经常在纸上画出九宫格就可以直接和小伙伴一起玩,规则就是谁先在九宫格中下出同一行三个一样,或者同一列三个一样,又或者对角线的三个一样的棋子,谁就获胜:其实在编程中,代码也能实现人机版的三子棋小游戏,让我们一起来看看如何实现的吧! 二.游戏设计步骤 将三子棋游戏的实现逻辑分为以下步骤: 1.创建菜单(选择玩游戏或者退出游戏) 2.创建和初始化游戏棋盘(本质上为一个二维数组) 3.打

  • 基于C语言利用哈夫曼树实现文件压缩的问题

    一.哈夫曼树 具有n个权值的n个叶子结点,构造出一个二叉树,使得该树的带权路径长度(WPL)最小,则称此二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree). 注意:哈夫曼树是带权路径长度最短的树,且权值越大的叶子结点离根结点越近. 二.哈夫曼编码         哈夫曼编码是一种编码方式,又称"霍夫曼编码",其是可变字长的编码(VCL)的一种,是由霍夫曼于1952年提出的一种编码方式,有时被称为最佳编码,一般称为Huffman编码. 那么我们为什么要使用哈夫曼编码进行压缩?

  • C++ 哈夫曼树对文件压缩、加密实现代码

    在以前写LZW压缩算法的时候,遇到很多难受的问题,基本上都在哈夫曼编码中解决了,虽然写这代码很费神,但还是把代码完整的码出来了,毕竟哈夫曼这个思想确实很牛逼.哈夫曼树很巧妙的解决了当时我在LZW序列化的时候想解决的问题,就是压缩后文本的分割.比如用lzw编码abc,就是1,2,3.但这个在存为文件的时候必须用分割符把1,2,3分割开,非常浪费空间,否则会和12 23 123 产生二义性.而哈夫曼树,将所有char分布在叶节点上,在还原的时候,比如1101110,假设110是叶节点,那么走到110

  • C语言实现哈夫曼树

    本文实例为大家分享了C语言实现哈夫曼树的具体代码,供大家参考,具体内容如下 //哈夫曼树C语言实现 #include <stdio.h> #include <stdlib.h> typedef struct HuffmanNode { char letter;//存储的字符,叶节点为字母,非叶节点为# struct HuffmanNode *parent;//父亲结点 int code;//如果为父亲结点的左孩子,则为0,右孩子为1 }HuffmanNode; typedef st

  • C语言实现哈夫曼树的方法

    本文实例为大家分享了C语言实现哈夫曼树的具体代码,供大家参考,具体内容如下 准备工作: 1.定义一个结构体,表示一个节点.其中,这个结构体有4个成员变量,分别表示是这个节点的权值,父节点及左右子节点的下标 2.定义一个整形数组,用于存放各个节点的权值 3.定义一个整形数组,用于存放哈夫曼编码,当然也可以定义一个整形数组来存放哈夫曼编码 构建哈夫曼树: 1.给这个哈夫曼树创建一个结构体数组,其中分配的空间是2 * n - 1,因为我们都知道哈夫曼树的性质有一个是:给定n个叶子节点,那么由这n个叶子

  • Java利用哈夫曼编码实现字符串压缩

    赫夫曼编码基本介绍 1) 赫夫曼编码也翻译为 哈夫曼编码(Huffman Coding),又称霍夫曼编码,是一种编码方式, 属于一种程序算法 2) 赫夫曼编码是赫哈夫曼树在电讯通信中的经典的应用之一. 3) 赫夫曼编码广泛地用于数据文件压缩.其压缩率通常在 20%-90%之间 4) 赫夫曼码是可变字长编码(VLC)的一种.Huffman 于 1952 年提出一种编码方法,称之为最佳编码 在通信领域中几种信息处理方式的区别(以字符串" i like like like java do you li

  • C语言实现哈夫曼树的构建

    哈夫曼树(霍夫曼树)又称为最优树. 1.路径和路径长度 在一棵树中,从一个结点往下可以达到的孩子或孙子结点之间的通路,称为路径.通路中分支的数目称为路径长度.若规定根结点的层数为1,则从根结点到第L层结点的路径长度为L-1. 2.结点的权及带权路径长度 若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权.结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积. 3.树的带权路径长度 树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL #include

  • 基于C++实现的哈夫曼编码解码操作示例

    本文实例讲述了基于C++实现的哈夫曼编码解码操作.分享给大家供大家参考,具体如下: 哈夫曼编码是一个通过哈夫曼树进行的一种编码,一般情况下,以字符:'0'与'1'表示.编码的实现过程很简单,只要实现哈夫曼树,通过遍历哈夫曼树,这里我们从每一个叶子结点开始向上遍历,如果该结点为父节点的左孩子,则在字符串后面追加"0",如果为其右孩子,则在字符串后追加"1".结束条件为没有父节点.然后将字符串倒过来存入结点中. C++实现代码如下: #include<iostre

  • C++详解哈夫曼树的概念与实现步骤

    目录 一. 基本概念 二.构造哈夫曼树 三.哈夫曼树的基本性质 四.哈夫曼编码 五.哈夫曼解码 六.文件的压缩和解压缩 一. 基本概念 结点的权: 有某种现实含义的数值 结点的带权路径长度: 从结点的根到该结点的路径长度与该结点权值的乘积 树的带权路径长度: 树上所有叶结点的带权路径长度之和 哈夫曼树: 在含有 n n n个带权叶结点的二叉树中, w p l wpl wpl 最小 的二叉树称为哈夫曼树,也称最优二叉树(给定叶子结点,哈夫曼树不唯一). 二.构造哈夫曼树 比较简单,此处不赘述步骤

  • C++使用数组来实现哈夫曼树

    目录 写在前面 构造思想 算法设计 构造实例 理解代码 确定结构体 循环找出最小值 调用细节 调试试图 总结 写在前面 哈夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树.所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数).树的路径长度是从树根到每一结点的路径长度之和,记为WPL=(W1*L1+W2*L2+W3*L3+...+Wn*Ln),N个权值Wi(i=1,2,...n)构成一棵有N个叶结点的二叉树,相应的叶

  • 使用C语言详解霍夫曼树数据结构

    1.基本概念 a.路径和路径长度 若在一棵树中存在着一个结点序列 k1,k2,--,kj, 使得 ki是ki+1 的双亲(1<=i<j),则称此结点序列是从 k1 到 kj 的路径. 从 k1 到 kj 所经过的分支数称为这两点之间的路径长度,它等于路径上的结点数减1. b.结点的权和带权路径长度 在许多应用中,常常将树中的结点赋予一个有着某种意义的实数,我们称此实数为该结点的权,(如下面一个树中的蓝色数字表示结点的权) 结点的带权路径长度规定为从树根结点到该结点之间的路径长度与该结点上权的乘

随机推荐