无论是在我们的开发项目中,还是在我们的日常生活中,都会较多的涉及到文件压缩。谈到文件压缩,可能会有人想问文件压缩到底是怎么实现的,实现的原理是什么,对于开发人员来说,怎么实现这样一个压缩的功能。

接下来,我们就来了解一下文件压缩的相关知识。文件压缩是如何实现的?这个我们就得了解一下数据结构,因为文件在压缩的过程中会转化为数据流,那么如何将数据流进行对应的压缩,这个问题就得靠算法来实现。那么文件压缩的算法是什么呢?那就是HuffmanTree。

提到HuffmanTree,我们就需要顺道讲讲数据结构和算法。在计算机中,数据结构和算法可以说是程序的灵魂。

数据结构:是相互之间存在一种或多种特定关系的数据元素的集合。按照视点的不同,我们将数据结构分为逻辑结构和物理结构。

(1).逻辑结构:是指数据对象中数据元素之间的相互关系。逻辑结构包含:集合结构(集合结构中的数据元素除了同属于一个集合外,他们之间没有其他关系);线性结构(线性结构中的数据元素之间是一对一的关系);树形结构(树形结构的数据元素之间存在一种一对多的层次关系);图形结构(图形结构的数据元素是多对多的关系)。

(2).物理结构:是指数据的逻辑结构在计算机中的存储形式。物理结构包含:顺序存储结构(是把数据元素存放在地址连续的存储单元里,其数据间的逻辑关系和物理关系是一致的);链式存储结构(是指把数据元素存放在任意的存储单元里,这组存储单元可以是连续的,也可以是不连续的)

上面介绍了一下数据结构的分类,当然,说到HuffmanTree,那就需要提一下“树形结构”。

树:是N(N大于或等于0)个节点的有限集合。现在介绍一下树的三种表示法:

(1).双亲表示法(在每个节点中,附设一个指示器指示双亲节点到链表中的位置);

(2).孩子表示法(每个节点有多个指针域,其中每个指针指向一个棵树的根节点,我们把这种方法叫做链表表示法);

(3).孩子兄弟表示法(任意一棵树,它的第一个孩子如果存在就是唯一的,它的友兄弟如果存在也是唯一的,因此,我们设置两个指针,分别指向该节点的第一个孩子和此节点的又兄弟)。

上面提到树,现在介绍一下二叉树。

二叉树:是N(N大于或等于0)个节点的有限集合,该集合或者为空,或者有一个根节点和两棵互不相交的、分别称为根节点的左子树和右子树的二叉树组成。

接下来介绍一下几种特殊的二叉树:

(1).斜树:所有的节点都只有在左子树的二叉树叫做左斜树。所有节点都是只有右子树叫做右斜树。

(2).满二叉树:在一棵二叉树中,如果所有分支节点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树成为满二叉树。

(3).完全二叉树:对一棵具有N个节点的二叉树按层序编号,如果编号为I(1大于或等于I小于或等于N)的节点与同样深度的满二叉树中编号为I的节点在二叉树中位置完全相同,则这棵二叉树成为完全二叉树。

前面我首先介绍了数据结构的定义和分类,接着介绍了树,二叉树。最后让我们一起来具体的了解一下HuffmanTree。

从树中的一个节点到另一个节点之间的分支构成两个节点之间的路径,路径上的分支数目称做路径长度。树的路径长度就是从树根到每一个节点的路径长度之和。节点的带权的路径长度为从该节点到跟之间的路径长度与节点上权的乘积。树的带权路径长度为树中所有叶子节点的带权路径长度之和。

HuffmanTree:带权路径长度WPL最小的二叉树称做赫夫曼树。(又称做:最优二叉树)

赫夫曼编码:规定赫夫曼树的左分支代表0,又分支代表1,则从根节点到叶子节点所经过的路径分支组成的0和1的序列便为该节点对应字符的编码。

以上介绍了 HuffmanTree的相关概念知识,现在就需要将这个数据结构采用代码实现,接下来提供一段采用C#代码实现的 HuffmanTree。

1.位流的基类:

  1. /// <summary>
  2. /// 位流的基类。
  3. /// </summary>
  4. /// <remarks>
  5. /// 一个字节流转换到一个位流的规则实现之间。
  6. /// </remarks>
  7. public abstract class BitStream
  8. {
  9. /// <summary>
  10. /// 在数据流上快速的获取最大位数
  11. /// </summary>
  12. public abstract int MaxReadAhead { get; set; }
  13.  
  14. /// <summary>
  15. /// 从流中读取位。
  16. /// </summary>
  17. /// <param name="count">读取的比特数。</param>
  18. /// <returns>位为UInt32。</returns>
  19. public abstract uint Read(int count);
  20.  
  21. /// <summary>
  22. /// 在流上查询数据
  23. /// </summary>
  24. /// <param name="count">查询的位数。</param>
  25. /// <returns>位为UInt32。</returns>
  26. /// <remarks>此方法不消耗位(即移动文件指针)。</remarks>
  27. public abstract uint Peek(int count);
  28.  
  29. /// <summary>
  30. /// 从流中消耗比特,而不返回它们。
  31. /// </summary>
  32. /// <param name="count">消耗的比特数。</param>
  33. public abstract void Consume(int count);
  34. }

2.哈夫曼树的实现:

  1. /// <summary>
  2. ///哈夫曼树的实现。
  3. /// </summary>
  4. /// <remarks>
  5. /// 创建一个查找表,将采取任何位序列(最大树深度的长度),指示输出符号。在WIM文件,在实践中,没有一块超过32768字节
  6. ///长度,所以我们经常会产生一个更大的查找表比它的数据编码。这使得异常快速符号查找O(1),但效率较低整体。
  7. /// </remarks>
  8. public sealed class HuffmanTree
  9. {
  10. // 每个符号的最大位
  11. private readonly int _numBits;
  12.  
  13. // 最大符号
  14. private readonly int _numSymbols;
  15.  
  16. private readonly uint[] _buffer;
  17.  
  18. public HuffmanTree(uint[] lengths)
  19. {
  20. Lengths = lengths;
  21. _numSymbols = lengths.Length;
  22.  
  23. uint maxLength = ;
  24. for (var i = ; i < Lengths.Length; ++i)
  25. {
  26. if (Lengths[i] > maxLength)
  27. {
  28. maxLength = Lengths[i];
  29. }
  30. }
  31.  
  32. _numBits = (int)maxLength;
  33. _buffer = new uint[ << _numBits];
  34.  
  35. Build();
  36. }
  37.  
  38. public uint[] Lengths { get; set; }
  39.  
  40. public uint NextSymbol(BitStream bitStream)
  41. {
  42. var symbol = _buffer[bitStream.Peek(_numBits)];
  43.  
  44. // 我们可能在读,复位比特流的位置
  45. bitStream.Consume((int)Lengths[symbol]);
  46.  
  47. return symbol;
  48. }
  49.  
  50. private void Build()
  51. {
  52. var position = ;
  53.  
  54. //对于每一位长度…
  55. for (var i = ; i <= _numBits; ++i)
  56. {
  57. // 检查每个符号
  58. for (uint symbol = ; symbol < _numSymbols; ++symbol)
  59. {
  60. if (Lengths[symbol] != i) continue;
  61. var numToFill = << (_numBits - i);
  62. for (var n = ; n < numToFill; ++n)
  63. {
  64. _buffer[position + n] = symbol;
  65. }
  66.  
  67. position += numToFill;
  68. }
  69. }
  70.  
  71. for (var i = position; i < _buffer.Length; ++i)
  72. {
  73. _buffer[i] = uint.MaxValue;
  74. }
  75. }
  76. }

赫夫曼树和赫夫曼编码对于带权路径的二叉树做了一些了解,用于初步理解压缩原理。对于数据结构的理解,需要我们花费很多的时间,也需要我们在这些数据结构中做一个细致的分类。

HuffmanTree的浅析和在C#中的算法实现的更多相关文章

  1. 浅析busybox-1.12.0中ash的脚本命令局限性

    浅析busybox-1.12.0中ash的脚本命令局限性 LUTHER= 表示将LUTHER清空,将其变为null echo ${LUTHER:-111}如果执行该句之前LUTHER变量不存在,那么显 ...

  2. InnoDB的锁机制浅析(二)—探索InnoDB中的锁(Record锁/Gap锁/Next-key锁/插入意向锁)

    Record锁/Gap锁/Next-key锁/插入意向锁 文章总共分为五个部分: InnoDB的锁机制浅析(一)-基本概念/兼容矩阵 InnoDB的锁机制浅析(二)-探索InnoDB中的锁(Recor ...

  3. 浅析C#组件编程中的一些小细节

    控件与组件的区别(Control&Component的区别) 作者:作者不详  发布日期:2011-06-30 12:08:41 控件与组件的区别(Control&Component的 ...

  4. 浅析网页meta标签中X-UA-Compatible属性的使用

    今天有一个做开发的朋友突然问你知道很多网站上面加入的X-UA-Compatible属性的意义么?其实这个在以前还专门花了一点时间来验证我自己的想法,结果也确实如自己所预想的那样,八九不离十,当然有一点 ...

  5. 浅析负载均衡的6种算法,Ngnix的5种算法。

    浅析负载均衡的6种算法,Ngnix的5种算法.浮生偷闲百家号03-21 10:06关注内容导读其实际效果越来越接近于平均分配调用量到后端的每一台服务器,也就是轮询的结果.源地址哈希的思想是根据获取客 ...

  6. Atitit.软件中见算法 程序设计五大种类算法

    Atitit.软件中见算法 程序设计五大种类算法 1. 算法的定义1 2. 算法的复杂度1 2.1. Algo cate2 3. 分治法2 4. 动态规划法2 5. 贪心算法3 6. 回溯法3 7. ...

  7. 【转】你真的理解Python中MRO算法吗?

    你真的理解Python中MRO算法吗? MRO(Method Resolution Order):方法解析顺序. Python语言包含了很多优秀的特性,其中多重继承就是其中之一,但是多重继承会引发很多 ...

  8. STL中的算法小结

    ()要运用STL的算法,首先必须包含头文件<algorithm>,某些STL算法用于数值处理,因此被定义于头文件<numeric> ()所有STL算法都被设计用来处理一个或多个 ...

  9. 机器学习中的算法-决策树模型组合之随机森林与GBDT

    机器学习中的算法(1)-决策树模型组合之随机森林与GBDT 版权声明: 本文由LeftNotEasy发布于http://leftnoteasy.cnblogs.com, 本文可以被全部的转载或者部分使 ...

随机推荐

  1. Unity - Apk包的代码与资源提取

    最近在研究如何给Unity游戏进行加密,让别人不能轻易破解你的apk包,不过网上的加密方法都是有对应的破解方法~_~!!结果加密方法没找到好的,逆向工程倒会了不少.今天就来讲解如何提取一个没做任何保护 ...

  2. PHP文件相关的操作函数——文件操作

    1.文件的代开与关闭 1.1 fopen() 作用:该函数用于打开一个文件 具体使用访问:http://www.w3school.com.cn/php/func_filesystem_fopen.as ...

  3. Eclipse上安装GIT插件EGit及使用

    一.Eclipse上安装GIT插件EGit Eclipse的版本eclipse-java-helios-SR2-win32.zip(在Eclipse3.3版本找不到对应的 EGit插件,无法安装) E ...

  4. IEEE/ACM ASONAM 2014 Industry Track Call for Papers

    IEEE/ACM International Conference on Advances in Social Network Analysis and Mining (ASONAM) 2014 In ...

  5. 利用结果集元数据将查询结果封装为map

    package it.cast.jdbc; import java.sql.Connection; import java.sql.ParameterMetaData; import java.sql ...

  6. Redis 的性能幻想与残酷现实

    2011 年,当初选择 Redis 作为主要的内存数据存储,主要吸引我的是它提供多样的基础数据结构可以很方便的实现业务需求.另一方面又比较担心它的性能是否足以支撑,毕竟当时 Redis 还属于比较新的 ...

  7. IT人生知识分享:概率与运气

    前言: 最近的人生多了些体验,也读了些许书,感觉还是有些知识是可以分享的. 今天难得周六,特意开电脑了,花几个小时写写,和大伙分享分享点知识. 以下内容,更多的需要读者思考,所以结论不会写太清晰,但一 ...

  8. HTML和CSS经典布局5

    如下图: 需求: 1. 如图 2. 可以从body标签开始. 3. 页面内容高度设置高点,把窗口的滚动条显示出来,但是busy indicator不滚动. <!DOCTYPE html> ...

  9. .NET中那些所谓的新语法之三:系统预定义委托与Lambda表达式

    开篇:在上一篇中,我们了解了匿名类.匿名方法与扩展方法等所谓的新语法,这一篇我们继续征程,看看系统预定义委托(Action/Func/Predicate)和超爱的Lambda表达式.为了方便码农们,. ...

  10. 启动 Apache2.2 的问题

    启动 Apache2.2 的问题: windows不能在本地计算机启动 Apache2.2.有关更多信息,查阅系统事件日志.如果这是非Microsoft服务,请与服务厂商联系,并参考特定服务错误代码1 ...