`
GLC
  • 浏览: 112698 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
社区版块
存档分类
最新评论

哈弗曼简单实现压缩

阅读更多

哈弗曼简单实现压缩

了解哈弗曼树就先从简单的二叉树讲起:
   二叉树:有一个根结点及其叶子结点组成、一个结点下只能接两个叶子结点:分别叫做左子树和右子树;其存储信息和链表的结点大致相似。
如图所示:



[size=large]
二叉树的遍历可分为四种:先序遍历、中序遍历、后序遍历以及层次遍历。

  先序遍历:先访问根结点再访问左子树、右子树。

  中序遍历:先访问左子树、再访问根结点、右子树。

  后序遍历:先访问左子树、右子树、根结点。

  层次遍历:一层一层从上往下、从左往右遍历。

  区分先、中后序遍历的最好方法是根据左子树遍历的先后;而且左子树始终在右子树前面。

了解二叉树后、我们需要知道权值的概念:在数据结构中,权值就是定义的路径上面的值。

例如长沙,广州分别是两个端点,两个端点可以用边相连表示可达,长沙到广州800km,那么就说边的权值是800.




这样、二叉树通过赋权值使得其形成的权值不同;而我们做哈弗曼压缩时、需要的是权值最小

的树即最优树。
例:




这样、压缩所需要的哈弗曼树有了,接着我们要遍历压缩的内容并用数组统计其出现的频率、

统计完后、通过数组中统计的频率构造哈弗曼树;在构造过程中、生成好的二叉树将叶子节点

以左0右1的方式分配编码;再用数组存装对应字符及其相应哈弗曼编码、压缩时,读入头文

件、将数组中哈弗曼编码长度写入压缩文本、接着将每个字符的对应的哈弗曼编码连接;当大

于8个字节时化成整形写入文件;最后不足8个时需要补0.这就是压缩头文件。。。
例:

// 创建文件输入流对象
		FileInputStream fis = new FileInputStream(path);
		// //创建文本缓冲流对象
		// BufferedInputStream bis=new BufferedInputStream(fis);
		// //实例化一个缓冲输出流对象
		// BufferedOutputStream bos=new BufferedOutputStream(fos);
		// 实例化一个输出流对象
		FileOutputStream fos = new FileOutputStream(path + "压缩文件.txt");

		// 写出每一个字节对应编码的长度
		for (int j = 0; j < saveCode.length; j++) {
			// System.out.println("请输出对应节点的长度" + saveCode[j]);
			// if (saveCode[j].getLength() != 0) {
			// 先 写入asc码所对应的下表
			// fos.write(j);
			// 写入asc码中每个编码的01串长度
			fos.write(saveCode[j].getLength());
			System.out.println("字符"+((char)j)+"的编码:" + "------->"
					+ saveCode[j].getNode());
			// 将01串的长度进行累加
			reminder += saveCode[j].getLength();
			// }
		}
//		// 计算出补零的个数,并读入
//		int remind = reminder % 8;
//		// 写入文件,并将remind进行强转
//		fos.write(remind);
//		System.out.println("jjjjjjjjjjjjjjj" + remind);
		/*********************** 写入头文件 **************************************/

		// 先声明asc编码的下表
		int i = 0;
		// 声明缓冲区得到的01串编码
		String writes = "";
		// 声明要转的01串字符
		String waiteString = "";
		// 声明一个01串的中转站
		String tranString = "";
		// 声明01串的个数
		int count = 0;
		// 进行循环,将sac编码下所有01串字符转化成int型写入文件
		while ((i < 256) || (count >= 8)) {
			// 如果缓冲区的01串大于8// 如果大于等于8,则写入文件
			if (count >= 8) {
				// 先将waiteString清零
				waiteString = "";
				// 先取出前8个01串字符
				for (int j = 0; j < 8; j++) {
					waiteString += writes.charAt(j);
				}

				// 将取出的01串转化为int型,且写入文件
//				System.out.println("><><><><><><" + waiteString);
//				
//				System.out.println("&&&&&&&&&&&&&"+changeString(waiteString));
				// 判断writes的01串是等于还是大于8
				if (writes.length() > 8) {
					//清空中转字符
					tranString="";
					// 将writes的01串减8
					for (int t = 8; t < writes.length(); t++) {
						// 将剩下的01串移到tranString上
						tranString += writes.charAt(t);
					}
                    //再将writes清空
					writes="";
					// 再将writes指针指回剩下的01串
					writes = tranString;
				}
				// 如果是等于8
				else {
					writes = "";
				}
				// 再将count数量减8
				count -= 8;
				//定义一个变量来接收转化成int型的数
				int intw=changeString(waiteString);
				fos.write(intw);
			// 如果01串不足8个
			}else {
				// System.out.println("--------->");
				// 判断对应asc编码是否存在
				// if(saveCode[i]!=null){
				// 得到01串的个数
				count += saveCode[i].getLength();
				// 得到01串编码
				writes += saveCode[i].getNode();
//				System.out.println(saveCode[i].getLength() + "<><><><><><><><>"
//						+ saveCode[i].getNode());
				// }
				System.out.println("i:"+i+">>>>>>>>>>"+saveCode[i].getNode());
				i++; 
			}
		}
		System.out.println("跳出循环");
		// 最后循环累加之后、剩下不足8个01串,则进行补0

		if (count > 0) {
			// 定义一个计数器;记录补零的个数
//			int k = 0;
			waiteString = "";// 清空要转化的的码
			for (int t = 0; t < 8; t++) {
				if (t < writes.length()) {
					waiteString = waiteString + writes.charAt(t);
//					System.out.println("**********************" + t);
				} else {
//					k++;
					waiteString = waiteString + '0';
				}
			}
			fos.write(changeString(waiteString));// 写入
			// 写入头文件补0 的个数
			// fos.write(k);
//			System.out.println("llllllllllll" + k);
			System.out.println("写入了->" + waiteString);
		}



压缩文件内容:一边遍历文件内容、一边将读到的字节一个一个地转成哈弗曼编码、再连接;

当大于8个01串长度时就进行转化;并将转化后的int型写入。写入之后要记得删除以转化的

01串;这样转化直至文件末尾;如果文件末尾01串长度不足8位、则需要补0;并将补0的个

数写入文件;

/*************** 写入文件内容 ************************************************/
		// 得到读入字符的ascii编码的值
		int idata = fis.read();
//		System.out.println("文件内容是:" + idata);
		// 缓冲区得到的01串编码
		writes = "";
		// 要转的01串字符
		tranString = "";
		// 01串的个数
		count = 0;
		// 文件没有读完的时候
		while ((fis.available() > 0) || (count >= 8)) {
			// 如果缓冲区的01串码大于等于8,则进行转化
			if (count >= 8) {
				// 先将waiteString清零
				waiteString = "";
				// 截取前8个01串并转化
				for (int t = 0; t < 8; t++) {
					waiteString += writes.charAt(t);
				}
				// 将取得的01串转化并写入
//				System.out.println("转化的字符是:" + waiteString);
				// 判断01串是大于8还是等于8
				if (writes.length() > 8) {
					// 01串的中转站
					tranString = "";
					// 再将01串减8
					for (int t = 8; t < writes.length(); t++) {
						tranString += writes.charAt(t);
					}
					writes="";
					// 将writes指针指回
					writes = tranString;
					System.out.println("打印出剩下的01串:" + writes);
				} else {
					// 如果等于8
					writes = "";
				}
				// 将前八个01串删除
				count -= 8;
				int intw=changeString(waiteString);
				fos.write(intw);

			} else {
				// 读入idata字节,对应编码写出信息

				count = count + saveCode[idata].getLength();
				// 将字符所对应的01串编码进行累加
				writes += saveCode[idata].getNode();
//				System.out.println("<---------------->" + count + "===="
//						+ writes);
				// 将idata往下移
				idata = fis.read();
//				System.out.println("//////" + saveCode[idata].getLength()
//						+ "//////" + saveCode[idata].getNode());
			}
		}
		// 再将读取的字符的01串长度进行累加
		count += saveCode[idata].getLength();
		// 将01串进行累加
		writes = writes + saveCode[idata].getNode();

		// 记录补零的个数
		int endsint = 0;
		if (count > 0) {
			// 清空待转化的字符
			waiteString = "";
			// 将剩下的01串转到waiteString上
			for (int t = 0; t < 8; t++) {
				if (t < writes.length()) {
					waiteString = waiteString + writes.charAt(t);
				} else {// 剩下的补零
					waiteString = waiteString + '0';
					endsint++;
				}
			}
			// 补完0 后,进行转化写入
			fos.write(changeString(waiteString));
			System.out.println("最后的字符已经写入了!!!!");
		}
//		System.out.println("ttttttttttt" + endsint);
		// 将补零的个数写入
		fos.write(endsint);
		System.out.println("压缩完毕。。。。");
		fis.close();
		fos.close();



解压的过程是一个逆反过程:也是先读出头文件、再读出文件内容并读出的内容转成01串与读出的头文件匹配、、如果匹配成功则写入文件、值得注意的是倒数第二个有补0的情况、这就需要将最后一位补零个数的字节读出来;减去。这样再看是否能继续转化。。。

java.io.FileInputStream fis = new java.io.FileInputStream(path2);
		// 实力化输入流对象
		java.io.FileOutputStream fos = new java.io.FileOutputStream(path2 + "解压文件.txt");
		// 读入文件,得到asc码数组的值
		for (int i = 0; i < 256; i++) {
			// 先读出asc码下表
			Code code = new Code();
			// 读出每个ascii码的01串长度
			code.setLength(fis.read());
			code.setNode("");
			// 加入到数组中
			codeArray[i] = code;
			// System.out.println("--------->"+i+codeArray[i].getLength());
		}
//		int reminder = fis.read();
//		System.out.println("编码长度读取完毕" + reminder);
		/********************** 读入头文件 **********************/

		// 解压头文件,得到 sacii码的01串//读SaveCode中0~256字节的的数据
		// 声明一个从文件中读出的com变量
		String coms = "";
		// 声明一个从0开始计数的变量
		int i = 0;
		while (i < 256) {
			// 当缓存区的编码比codeArray[i]的长度长时
			if (coms.length() >= codeArray[i].getLength()) {
				// System.out.println("================="+i+":"+coms+"<>"+codeArray[i].getLength());
				// 取得该编码
				for (int t = 0; t < codeArray[i].getLength(); t++) {
					// 将coms上的字符转到codeArray[i]的node中
					codeArray[i].setNode(codeArray[i].getNode()
							+ coms.charAt(t));
				}
//				System.out.println("现在"+((char)i)+"的编码是:" + codeArray[i].getNode());
				// 去掉coms前几位
				String coms2 = "";
				for (int j = codeArray[i].getLength(); j < coms.length(); j++) {
					coms2 += coms.charAt(j);
				}
				coms="";
				// 将coms指针回指
				coms = coms2;
				 System.out.println("<><><><><><><>"+i+codeArray[i].getNode());
				// 往后移
				
				i++;
				
			}
			// 如果没有写入的话,再读入一个byte化为01串
			else {
				// System.out.println("-------->"+coms);
				coms = coms + intToString(fis.read());
				// System.out.println("<><><><><><><><><><><"+coms);
			}


[/size]


如果浏览还不清楚、可以下载源代码;谢谢指教
  • 大小: 121.5 KB
  • 大小: 72.9 KB
分享到:
评论

相关推荐

    HaffmanCode.rar_哈弗曼 压缩算法_哈弗曼c++_数据压缩

    它是基于频率的前缀编码方法,通过构建一棵特殊的二叉树(哈弗曼树)来实现字符或符号的编码,使得频繁出现的字符具有较短的编码,不常出现的字符具有较长的编码,从而达到平均码长最短、压缩效率最高的目的。...

    yuesefuhuan.zip_哈弗曼树压缩

    它是基于贪心策略构建的一种特殊的二叉树结构,用于实现哈弗曼编码,这是一种有效的无损数据压缩方法。在哈弗曼编码中,频率较高的字符用较短的编码,而频率较低的字符用较长的编码,这样可以最大限度地压缩数据,...

    哈弗曼压缩解压程序源代码及注释

    哈弗曼编码是一种高效的数据压缩方法,由美国计算机科学家戴维·艾伦·哈弗曼在1952...尽管如此,由于其简单性和有效性,哈弗曼编码在许多压缩算法中仍然占有一席之地,特别是在需要快速实现简单压缩和解压缩的场景下。

    哈弗曼编码的实现

    以下是一个简单的实现流程: 1. 定义`HuffmanNode`结构体,包括字符、频率以及指向左右子节点的指针。 2. 使用`std::priority_queue`存储`HuffmanNode`,并初始化每个字符节点。 3. 通过合并两个最小节点构建哈弗曼...

    指针实现哈弗曼树

    尽管这个实现的功能比较简单,但已经足够理解哈弗曼树的基本原理和指针在其中的应用。对于实际的压缩应用,可能还需要考虑如何存储和解码哈弗曼编码,以及如何处理动态变化的字符频率等问题。不过,这样的基础实现对...

    哈弗曼编码的简单实现

    哈弗曼编码是一种高效的数据编码方法,主要用于无损数据压缩。它通过构建一棵特殊的二叉树——哈弗曼树(也称为最优二叉树),来为每个字符或符号分配一个唯一的二进制编码,使得频繁出现的字符拥有较短的编码,不常...

    哈弗曼编码__压缩任意格式文件[参考].pdf

    在给定的代码片段中,作者使用C++编写了一个简单的哈弗曼编码实现,能够压缩和解压缩任意格式的文件。程序通过读取文件计算字节频率,构造哈弗曼树,然后对文件进行编码和解码。作者还修复了一些已知的bug,例如只有...

    huffman--MATLAB哈弗曼压缩纯英文文本+图像Huffman编码

    在给定的文件中,我们有两个文件:一个是关于MATLAB实现的哈弗曼压缩纯英文文本的程序,另一个是关于图像的哈弗曼编码。MATLAB是一种强大的编程环境,尤其适用于数值计算和算法开发,因此它是实现哈弗曼编码的理想...

    课程实习时做的哈弗曼压缩程序

    它是基于一种特殊的二叉树结构——哈弗曼树(也称为最优二叉树),用于构建一个编码表,使得频繁出现的字符用较短的编码,不常出现的字符用较长的编码,从而实现数据的无损压缩。这种编码方式遵循“短码优先”的原则...

    jpeg.rar_JPEG图像压缩_jpeg matlab_哈弗曼_哈弗曼编码_游程

    这个“jpeg.rar”压缩包包含了关于JPEG图像压缩技术的实例,使用MATLAB编程语言实现,同时结合了哈弗曼编码和游程编码两种优化数据存储的技术。 首先,我们来详细解释JPEG压缩原理。JPEG主要采用的是离散余弦变换...

    C++实现哈弗曼编码与译码

    哈弗曼编码是一种可变字长编码方式,通过构造异字头的平均长度最短的码字来实现无损耗压缩。文章首先介绍了哈弗曼编码的原理,然后通过一个实例来描述哈弗曼编码的过程。接着,文章介绍了哈弗曼编码在C++中的实现,...

    哈弗曼压缩软件(数据结构试验报告附源程序).docx

    《哈弗曼压缩软件(数据结构试验报告附源程序).docx》的文件内容主要涉及一个数据结构课程设计项目,该项目是实现一个基于哈弗曼编码的文本压缩软件。哈弗曼编码是一种高效的前缀编码方法,常用于数据压缩,其原理是...

    数据结构课程设计 哈弗曼压缩+纸牌游戏

    实现哈弗曼压缩及解压缩功能,并计算压缩前后文件占用空间比 模型建立与题目分析 压缩: 以二机制可读形式打开源文件,以二进制可写形式打开压缩目标文件。每次从源文件读取八个比特(一字节),作为一个ASCII码...

    关于哈弗曼树的一个算法

    - **压缩效率高**:对于具有不同频率的字符,哈弗曼编码可以实现较高的压缩率,尤其对于文本、图像等数据,能显著减少存储空间。 - **解压速度快**:由于编码规则固定,解压缩过程相对简单,速度较快。 - **适应性强...

    哈弗曼树的建立 C++代码

    ### 哈弗曼树(Huffman Tree)的构建与C++实现 #### 知识点一:哈弗曼树的基本概念 ...总之,哈弗曼树的构建是数据压缩领域的一个经典问题,通过C++实现不仅能够加深对算法的理解,还能够在实际项目中发挥重要作用。

    C++哈弗曼树

    在压缩文件“哈弗曼树”中,可能包含了具体的C++代码实现、测试数据和运行结果。通过阅读和理解这段代码,你可以更深入地了解哈弗曼树的构建过程及其在C++中的实现细节。同时,这个压缩包也可以作为一个学习资源,...

    哈弗曼树的建立,以及编/译码的实现

    哈弗曼编码的特点是:频繁出现的字符拥有较短的编码,不常出现的字符则有较长的编码,从而在整体上实现高效的数据压缩。 哈弗曼编码的解码过程相对简单,只需按照编码的0和1序列从根节点出发,遇到0走左子树,遇到1...

    哈弗曼编码

    这种编码方式的核心在于通过构建一棵哈弗曼树来为每个字符分配一个唯一的二进制编码,以此实现对数据的有效压缩。哈弗曼编码是无损压缩的一种形式,意味着解压后的数据与原始数据完全一致。 #### 二、哈弗曼编码的...

Global site tag (gtag.js) - Google Analytics