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

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

    /// <summary>
/// 位流的基类。
/// </summary>
/// <remarks>
/// 一个字节流转换到一个位流的规则实现之间。
/// </remarks>
public abstract class BitStream
{
/// <summary>
/// 在数据流上快速的获取最大位数
/// </summary>
public abstract int MaxReadAhead { get; set; } /// <summary>
/// 从流中读取位。
/// </summary>
/// <param name="count">读取的比特数。</param>
/// <returns>位为UInt32。</returns>
public abstract uint Read(int count); /// <summary>
/// 在流上查询数据
/// </summary>
/// <param name="count">查询的位数。</param>
/// <returns>位为UInt32。</returns>
/// <remarks>此方法不消耗位(即移动文件指针)。</remarks>
public abstract uint Peek(int count); /// <summary>
/// 从流中消耗比特,而不返回它们。
/// </summary>
/// <param name="count">消耗的比特数。</param>
public abstract void Consume(int count);
}

2.哈夫曼树的实现:

    /// <summary>
///哈夫曼树的实现。
/// </summary>
/// <remarks>
/// 创建一个查找表,将采取任何位序列(最大树深度的长度),指示输出符号。在WIM文件,在实践中,没有一块超过32768字节
///长度,所以我们经常会产生一个更大的查找表比它的数据编码。这使得异常快速符号查找O(1),但效率较低整体。
/// </remarks>
public sealed class HuffmanTree
{
// 每个符号的最大位
private readonly int _numBits; // 最大符号
private readonly int _numSymbols; private readonly uint[] _buffer; public HuffmanTree(uint[] lengths)
{
Lengths = lengths;
_numSymbols = lengths.Length; uint maxLength = ;
for (var i = ; i < Lengths.Length; ++i)
{
if (Lengths[i] > maxLength)
{
maxLength = Lengths[i];
}
} _numBits = (int)maxLength;
_buffer = new uint[ << _numBits]; Build();
} public uint[] Lengths { get; set; } public uint NextSymbol(BitStream bitStream)
{
var symbol = _buffer[bitStream.Peek(_numBits)]; // 我们可能在读,复位比特流的位置
bitStream.Consume((int)Lengths[symbol]); return symbol;
} private void Build()
{
var position = ; //对于每一位长度…
for (var i = ; i <= _numBits; ++i)
{
// 检查每个符号
for (uint symbol = ; symbol < _numSymbols; ++symbol)
{
if (Lengths[symbol] != i) continue;
var numToFill = << (_numBits - i);
for (var n = ; n < numToFill; ++n)
{
_buffer[position + n] = symbol;
} position += numToFill;
}
} for (var i = position; i < _buffer.Length; ++i)
{
_buffer[i] = uint.MaxValue;
}
}
}

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

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. Insert or Merge && Insertion or Heap Sort

    原题连接:https://pta.patest.cn/pta/test/1342/exam/4/question/27102 题目如下: According to Wikipedia: Inserti ...

  2. MYSQL 5.0 USING BTREE 错误

    今天遇到个错误,导入数据库的时候报错,最后发现是php版本的问题导致包含 KEY `uniacid` (`uniacid`) USING BTREE 的SQL命令报错 5.1之前的写法: KEY `u ...

  3. 一鼓作气 博客--第三篇 note3

    1 推荐读书消费者行为学 -商业的本质,APP得到,5分钟商学院 2定义字典 dic={'name':haibao,'age':18} 3字典的基本操作--查询 dic={'name':'haibao ...

  4. 模拟Post登陆带验证码的网站

    前言: 作者在一个项目需求 模拟用户登陆,获取该用户的订单记录. 该系统需要用户名,密码,验证码 (验证码为正楷的数字4位),于是参考网络一些文章,并进行了很多测试,总结步骤如下: 步骤1 : 通过h ...

  5. 游戏编程技巧 - Type Object

    Type Object 使用场景 你在制作一款和LOL类似的游戏,里面有许多英雄,因此你想建立一个英雄基类,然后把各种英雄都继承自该基类,这些英雄类都有生命值和攻击力等属性.每次策划想增加一个英雄,你 ...

  6. window.onload与$(document).ready()的区别

    对于很多初学者来说,window.onload出现在代码中的频率非常高,这似乎变成了一种习惯,可是并不知道具体为什么要加这句代码,可以做几个试验对比: 实验一: <script> docu ...

  7. LVS DR模式 RealServer 为 Windows 2008 R2配置

    有3篇文档详细介绍 http://kb.linuxvirtualserver.org/wiki/Windows_Servers_in_LVS/DR_and_LVS/TUN_Clusters http: ...

  8. 深圳电信光纤用户必备:简单破解中兴ZXA10 F460光电猫,实现WIFI和自动拨号功能

    最近搬家,ADSL转成光纤宽带,下载速度非常给力.原来的ADSL路由器派不上用场,电信的安装人员也给开通了wifi功能,只是wifi无法上网,而且拨号一定需要用电脑连网线进行拨号.以前是直接把拨号账户 ...

  9. [PDO绑定参数]使用PHP的PDO扩展进行批量更新操作

    最近有一个批量更新数据库表中某几个字段的需求,在做这个需求的时候,使用了PDO做参数绑定,其中遇到了一个坑. 方案选择 笔者已知的做批量更新有以下几种方案: 1.逐条更新 这种是最简单的方案,但无疑也 ...

  10. 我只是想开个饭店—— JavaIO模型的演变

    Java的IO...真的是我所见过的高级语言中.最最复杂的... 看着这个图我也是醉了. 但是不知不觉间,java的IO已经更新到了NIO.2了,IO库早已经不止是这个样子了,那么这个过程中,它们经历 ...