`
利li香
  • 浏览: 37530 次
  • 性别: Icon_minigender_2
社区版块
存档分类
最新评论

哈弗曼压缩

xsx 
阅读更多

哈弗曼压缩的原理在这就不多说了,相信大家都了解(不了解的看有关资料),在这只讨论一下哈弗曼压缩步骤:

 

 

哈夫曼压缩:

1.读文件,统计文件中每个字节出现的次数,并用一个数组来保存

2.把数组中的每一个下标作为节点的元素(即为读取到的字节),下标对应的元素作为节点元素出现的次数, 构造节点优先队列

 

3.根据节点优先队列构建哈树

4.得到每个叶节点(即为字节)的编码

5.读文件,得到有序字符串

6.将有序字符串保存到字节数组中

7.将字节数组中的字节写入到文件当中

 

 

 

压缩:例如,获得了文件中的所有字节的字符串
  

1.如文件中:abbcccddddeeeee   假设文件是a.txt;
  

2.读文件,将读到的字节保存到一个int[]数组中
  

3.创建一个优先级队列将数组中的元素进行排序。
  

4.获得哈夫曼树的编码为:(且将编码 存到一个字符串数组当中即strs[256])
      a="010";--------strs[97]="010";
      b="011";--------strs[98]="011";
      c="00";---------strs[99]="00";
      d="10";---------strs[100]="10";
      e="11";---------strs[101]="11";
  

5.再次读文件得到文件中所有字节的有序编码字符串
     

allString="010 011011 000000 10101010 111111111111";
   

6.将allString字符串每8位每8位保存到一个字节数组byte[] 当中   
   
       如:  byte[0]=77;
       byte[1]=-127;
       byte[2]=85;
       byte[3]=-1;
       byte[4]=-128;
       byte[5]=7;
    
   

7.写文件,把字节数组写入到文件当中  假设写入的文件是b.txt;
   
   
    ----------就完成压缩了!!!
   
    以下是压缩代码:
   

public class YaSuo {

	/**
	 * 步骤一: 读取文件,统计文件中每个字节出现的次数,将字节出现的次数用一个int[] num = new int[256]保存
	 * 数组的下标表示字节,下标对应的元素表示该字节出现的次数
	 * 
	 * @param path
	 *            要读取的文件路径
	 * @return 返回用来存储字节次数的数组
	 */
	public int[] readFile(String path) {
		// 创建一个数组用来保存读到的字节 下标i 0-255是所有的字节 t表示每个字节出现的频率
		int[] num = new int[256];

		try {

			// 创建文件输入流用来读文件
			FileInputStream fis = new FileInputStream(path);// 此处抛出异常

			// 把输入流包装成缓冲流
			BufferedInputStream bis = new BufferedInputStream(fis);

			// 用缓冲流读字节,并把读到的字节保存到数组当中以便获得每个字节的频率
			while (bis.available() > 0) {// 当剩余字节不为0时

				int t = bis.read();// 缓冲流读到不同的字节就会返回不同的整型即0-255

				num[t]++;// 当读到相同的字节时频率就加1

				// 將獨到的字節設置成節點元素
				HfmNode node = new HfmNode(t, num[t]);
				// System.out.println("節點元素:"+(Integer)node.obj);
			}
		} catch (Exception ef) {
			ef.printStackTrace();
		}
		return num;
	}

	/**
	 * 步骤二: 当字节数组中元素不为0的时候,就创建一个树的节点对象, 将节点对象使用一个优先对象存储,来保证节点按照字节出现的次数排序
	 * 
	 * @param arr
	 *            用来保存字节出现次数的数组
	 * @return 返回保存节点的优先队列
	 */
	public PriorityQueue<HfmNode> array2Queue(int[] arr) {
		// 创建排序队列,自己制定比较器
		PriorityQueue<HfmNode> nodeList = new PriorityQueue<HfmNode>(11,
				new MyComparator());
		for (int i = 0; i < arr.length; i++) {
			if (arr[i] != 0) {// 如果出现的次数不是0,则创建节点
				// 创建节点对象
				HfmNode node = new HfmNode(i, arr[i]);
				nodeList.add(node);
			}
		}

		return nodeList;
	}

	// 实现比较器类
	class MyComparator implements Comparator<HfmNode> {// 按频率的大小进行比较
		public int compare(HfmNode h1, HfmNode h2) {
			return h1.getCount() - h2.getCount();

		}
	}

	/**
	 * 将优先级队列中的节点创建成哈夫曼树
	 * 
	 * @param nodeList
	 *            优先级队列
	 * @return 返回根节点
	 */
	public HfmNode createHfmTree(PriorityQueue<HfmNode> nodeList) {
		while (nodeList.size() > 1) {// 当优先级队列当中至少有两个节点时

			// 取出两个最小的节点
			HfmNode node1 = nodeList.poll();
			HfmNode node2 = nodeList.poll();
			// 有最小的节点创建合并的节点
			HfmNode addNode = new HfmNode(null, node1.getCount()
					+ node2.getCount());

			// 创建引用关系
			addNode.setLeft(node1);
			node1.setString("0");// 左节点编码为0

			addNode.setRight(node2);
			node2.setString("1");// 右节点编码为1

			node1.setLast(addNode);
			node2.setLast(addNode);
			// 将合并的节点加到优先队列当中
			nodeList.add(addNode);
		}

		HfmNode root = nodeList.peek();// 取出根结点

		return root;
	}

	/**
	 * 得到每个叶节点即字节的哈夫曼编码
	 * 
	 * 1.先得到每个叶节点的编码,将其保存在字符串数组中 如:String[] str=new String[256] 如str[97]="00011"
	 * str[98]="011111"
	 * 
	 * 
	 * 
	 */

	public String[] createByteCode(HfmNode root) {
		// 用一个字符串数组保存所有字符串编码如strs[97]="00011" str[98]="011111"
		// 如果定義為屬性的話,創建類對象時數組也會創建,浪費內存,所以把其放在方法中
		String[] strs = new String[256];
		Byte2String(strs, root);
		return strs;
	}

	private void Byte2String(String[] strs, HfmNode root) {

		if (root != null) {// 如果根節點不為空

			if (root.getLeft() != null) {// 如果左節點不為空,得到其編碼

				HfmNode leftNode = root.getLeft();// 得到左節點

				String str1 = root.getString() + leftNode.getString();// 左節點的編碼是父節點加自身節點

				leftNode.setString(str1);// 重新設置左節點編碼
				// System.out.println("獨到的字節"+(Integer) leftNode.getObject());
				// 將左節點的字節和編碼保存到字符串數組當中
				// 注意:是把葉節點的元素加進去,而不是所有創建樹的所有節點
				if (leftNode.getObject() != null) {
					// System.out.println("葉節點元素:"+(Integer)leftNode.getObject()+"葉節點字符串編碼:"
					// +leftNode.getString());
					strs[(Integer) leftNode.getObject()] = leftNode.getString();
				}
				// 遞歸調用
				Byte2String(strs, leftNode);
			}
			if (root.getRight() != null) {// 右節點不為空
				HfmNode rightNode = root.getRight();// 得到右節點
				// System.out.println("右節點的編碼:"+rightNode.getString());
				// 重新設置右節點編碼,即其編碼為父節點編碼加自身編碼
				String str2 = root.getString() + rightNode.getString();

				rightNode.setString(str2);
				// 將右節點的字節和編碼保存到字符串中
				if (rightNode.getObject() != null) {
					// System.out.println("葉節點元素:"+(Integer)rightNode.getObject()+"葉節點字符串編碼:"
					// +rightNode.getString());
					strs[(Integer) rightNode.getObject()] = rightNode
							.getString();
				}
				// 遞歸調用
				Byte2String(strs, rightNode);

			}
		}

	}

	/**
	 * 需要再次读文件,将文件中的所有字节按顺序用字节对应的编码表示,得到字符串 String allString=""; 如
	 * allString="0000010010101111100110101001111";
	 * 
	 * @param strs
	 * @return
	 */
	public String readAgain(String[] strs, String path) {
		// 文件中所有字節按順序對應的編碼
		String allString = "";
		try {
			// 创建文件输入流用来读文件
			FileInputStream fis = new FileInputStream(path);// 此处抛出异常

			// 把输入流包装成缓冲流
			BufferedInputStream bis = new BufferedInputStream(fis);

			// 用缓冲流读字节,并把读到的字节保存到数组当中以便获得每个字节的频率
			while (bis.available() > 0) {// 当剩余字节不为0时

				int t = bis.read();// 缓冲流读到不同的字节就会返回不同的整型即0-255

				// 存放从文件中读到的所有有序的字节编码
				allString = allString + strs[t];

			}

		} catch (Exception e) {
			e.printStackTrace();
		}
		return allString;

	}

	/**
	 * 将有序的字符串转化成字节
	 * 
	 * @param allString
	 *            所读的文件中字节转化成的字符串
	 * @return 返回转化的字节
	 */

	// 将有序的字符串保存到数组当中
	public byte[] toByte1(String allString){
		
		//先定义字节数组的长度
		int len=allString.length()/8+1;
		if(allString.length()%8!=0){
			len++;
			
		}
		//创建字节数组
		
		byte[] b=new byte[len];
		
		//判断字节数组最后一位的字节
		if(allString.length()%8==0){
			b[len-1]=0;
		}else{
			int length=8-allString.length()%8;
			b[len-1]=(byte) length;
			
			int buling=8-allString.length()%8;
			for(int i=0;i<buling;i++){
				allString=allString+"0";
			}
		}
		
		// 将字符串转化成字节保存到字节数组当中
		for (int i = 0; i < len-1; i++) {
			String duanString = allString.substring(i * 8, i * 8 + 8);
			b[i] = (byte)(Integer.parseInt(duanString, 2));
//System.out.println(duanString+"<>"+b[i]);
		}
		//System.out.println("字节数组的长度"+b.length);
		return b;
		
	}
	
	
	/**
	 * 写入文件的方法       输出流
	 * @param path  写入的文件路径
	 * @param bs    要写入的数据
	 */
	public static void writeFile(String path,byte[] bs){
		//方法分析:在写文件方法中,例如,在记事本上写数据(是字节),是从内存中将数据流出到记事本中
		 //步骤:1.建立一个文件输出流对象,指明要输出到的文件路径,相当于一个管道,连接内存和写的文件
		//       即java.io.FileOutputStream fos=new java.io.FileOutputStream(path
		//2.将内存中的数据写出来,因为事先在方法中已经定义路径和写的数据是字节数组类型
//		                   即//for(int i=0;i<bs.length;i++){
//    	              byte b=bs[i];
//    	             //写出字节
//    	           //问题write(byte[] b)
//                  //有的write()方法,并不是一个节一个字节的写出,而是存在缓存区中,当满了就输出
//    	               fos.write(b);
//                     }
		//3.将缓冲区中的数据清空及关闭输出流fos.flush();fos.close()
		try{
		//创建文件输出流
		java.io.FileOutputStream fos=new java.io.FileOutputStream(path);
		//将字符串转化成字节数组
		//byte[] bs=content.getBytes();
		    for(int i=0;i<bs.length;i++){
		    	byte b=bs[i];
		    	//写出字节
		    	//问题write(byte[] b)
              //有的write()方法,并不是一个字节一个字节的写出,而是存在缓存区中,当满了就输出
		    	fos.write(b);
		    }
		    //清空数据(内存缓冲区的)
		    fos.flush();
		    //关闭输出流
		    fos.close();
		
		
		
		}catch(Exception df){
			df.printStackTrace();
		}
	}
	/**
	 * 步骤七:将有序字节数组中的字节写入到文件当中
	 * @param path  写入的文件路径
	 * @param b   字节数组:  写入的字节
	 */
	  public void writeFile1(String path,byte[] b){
		  try{
		  //创建文件输出流
		  FileOutputStream fos=new FileOutputStream(path);
		
		  BufferedOutputStream bos=new BufferedOutputStream(fos);
		  
		  //遍历字节数组中的字节
		  
			  //有的write()方法,并不是一个字节一个字节的写出,而是存在缓存区中,当满了就输出
		  for(int i=0;i<b.length;i++){
		    	bos.write(b[i]);
		  }
		  bos.flush();//清空的是缓冲流中的东西,而不是输出流中的东西。注意啊!!!!!!!!!!!
		  fos.close();
		  }catch(Exception e){
			  e.printStackTrace();
		  }
		  
	  }
	
	
}

 

 

感悟:老师给我们讲了哈弗曼压缩原理,让自己写一个压缩软件,一开始不知道怎么入手,一点思路都没有,在老师的耐心指导下, 才真正理解哈弗曼压缩原理,在方法上也要遵循每写一个方法都要测试一下正确性,否则到最后一起测试的时候,找不出到底是哪个方法出错了!

分享到:
评论

相关推荐

    哈弗曼压缩总结

    哈弗曼压缩是一种高效的数据压缩方法,主要基于哈弗曼树(Huffman Tree)的构建。这个技术在信息编码和数据存储中具有广泛的应用,特别是在文本压缩、图像压缩和文件传输等领域。以下是对哈弗曼压缩相关知识点的详细...

    哈弗曼压缩解压器.rar

    在哈弗曼压缩过程中,首先需要统计输入文本中各个字符的出现频率,然后构建哈弗曼树。构建过程通常采用贪心算法,通过不断地合并频率最小的两个节点来逐步形成完整的树形结构。最后,根据树的结构生成每个字符的...

    哈弗曼压缩解压算法代码.rar

    在哈弗曼压缩过程中,首先需要建立一个哈弗曼树。这通常包括以下步骤: 1. **统计字符频率**:对输入的数据流进行分析,统计每个字符出现的次数。 2. **构建哈弗曼树**:创建一个空的优先队列,将每个字符作为一个带...

    哈弗曼压缩代码1

    哈弗曼压缩编码

    哈弗曼压缩解压文件

    在Java环境中实现哈弗曼压缩与解压文件,主要涉及以下几个关键知识点: 1. **哈弗曼树的构建**:哈弗曼树是一种带权路径长度最短的二叉树,权重小的节点通常位于树的顶层。构建哈弗曼树通常使用优先队列(如Java中...

    自适应哈弗曼压缩编码VC源码

    这个"自适应哈弗曼压缩编码VC源码"是使用Visual Studio 2010开发的一个工程,它提供了实现自适应哈弗曼编码算法的C++源代码。VS2010是一个流行的集成开发环境(IDE),支持C++编程,提供了一整套工具,包括编译器、...

    以字母为基础的哈弗曼压缩

    在C语言实现哈弗曼压缩的过程中,首先需要做的是统计输入文本中每个字符的出现频率。这个过程通常涉及读取文本文件,计算每个字符的词频,并将这些信息存储在一个字典或数组中。在这个案例中,`词频表_Character.txt...

    哈弗曼压缩解压算法VC++源码示例

    哈弗曼编码(Huffman Coding)是一种数据压缩算法,由美国计算机科学家大卫·艾尔曼在1952年提出。...在实际项目中,哈弗曼压缩算法常用于文本、图像和其他类型数据的压缩,以节省存储空间和提高传输效率。

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

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

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

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

    数据结构哈弗曼压缩课设

    在这个“数据结构哈弗曼压缩课设”中,你需要理解和应用以下几个关键知识点: 1. ASCII码:ASCII码(American Standard Code for Information Interchange)是计算机中字符的一种标准编码方式,包括数字、字母和...

    huffmanCompress哈弗曼压缩与解压缩

    huffmanCompress哈弗曼压缩与解压缩,一个压缩工具

    哈弗曼压缩编码报告1

    ### 哈弗曼压缩编码知识点详解 #### 实验项目名称 - **哈夫曼编码/译码器** #### 实验目的 本实验的主要目的是实现一个哈夫曼编码与解码的过程,即对输入的字符串进行哈夫曼编码,再对编码后的字符串进行解码,...

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

    在这个“课程实习时做的哈弗曼压缩程序”中,我们可以推测作者尝试实现了哈弗曼编码的基本流程,包括以下几个步骤: 1. **频率统计**:首先,需要对输入的文本或数据流中的字符进行频率统计,了解每个字符出现的...

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

    《哈弗曼压缩软件》是基于数据结构课程设计的一个项目,主要目的是让学生将所学的哈弗曼编码理论知识应用于实际问题中,提高编程技能。哈弗曼编码是一种高效的无损数据压缩方法,通过构建最优的二叉树(哈弗曼树)来...

    哈弗曼压缩和解压以及多项式的运算

    哈弗曼压缩的过程包括以下步骤: 1. **构建哈弗曼树**:首先统计每个字符的出现频率,然后用这些频率作为权重创建一个优先队列。接着,每次从队列中取出两个权重最小的节点,合并成一个新的节点,新节点的权重为两子...

    哈弗曼压缩解压算法,VC 源代码下载.rar

    在VC(Visual C++)环境中实现哈弗曼压缩和解压,通常涉及到以下几个关键步骤: 1. **频率统计**:首先,我们需要对输入的数据流进行分析,统计每个字符出现的频率。这是构建哈弗曼树的基础。 2. **构建哈弗曼树**...

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

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

    哈弗曼 压缩原理

    ### 哈弗曼压缩原理详解 #### 引言 数据压缩技术自其诞生以来,一直是信息技术领域的重要组成部分,尤其在大数据和网络通信时代显得更为关键。哈弗曼压缩算法,由D.A. Huffman在1952年提出,是一种基于统计特性...

    哈弗曼压缩算法

    在哈弗曼压缩过程中,首先需要统计输入文本中每个字符的出现频率,生成一个频率表。然后,使用这些频率来构造一棵哈弗曼树,这是一个特殊的二叉树,其中每个叶子节点代表一个字符,而内部节点表示合并后的频率。构建...

Global site tag (gtag.js) - Google Analytics