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

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

    # include <stdio.h> # include <malloc.h> # include <stdlib.h> //C语言实现栈 //结点 typede ...

  2. git学习笔记一

    一.概念理解 1.理解工作区和暂存区以及版本库 工作区我理解就是我们创建的程序所在的文件夹,比如test文件夹.其中有个.git文件,这个就是版本库,其中版本库中有个区域叫暂存区或叫索引. 截自廖雪峰 ...

  3. spring的代理模式

    静态代理: 首先定义一个接口,随便写一个方法 定义2个实现接口的方法 (被代理的对象) (代理对象) 需要将接口 定义get set 方法 代理增强的方法 然后实现 输出结果如下: 动态代理(jdk动 ...

  4. css确定元素水平居中和垂直居中

    ---恢复内容开始--- 首先,我们在了解如何通过css了解元素水平和垂直居中之前,先要了解下html都有哪些元素,这些元素与偶有哪些分类,因为不同类别的元素的水平垂直居中方法是完全不同的,究其根本当 ...

  5. 安卓初級教程(4):sqlite建立資料庫

    2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 ...

  6. Eclipse中自动提示的方法参数都是arg0,arg1的解决方法

    Eclipse中自动提示的方法参数都是arg0,arg1,就不能根据参数名来推断参数的含义,非常不方便. 解决方法:Preferences->Java->Installed JREs,发现 ...

  7. actor concurrency

    The hardware we rely on is changing rapidly as ever-faster chips are replaced by ever-increasing num ...

  8. 浅谈Android应用保护(一):Android应用逆向的基本方法

    对于未进行保护的Android应用,有很多方法和思路对其进行逆向分析和攻击.使用一些基本的方法,就可以打破对应用安全非常重要的机密性和完整性,实现获取其内部代码.数据,修改其代码逻辑和机制等操作.这篇 ...

  9. ASP.NET MVC 5 Web编程1 -- 入门

    开篇引言 说起ASP.NET MVC,我想作为WebForms开发者第一点要问的是:为什么要使用它?我的理解是:MVC是更细节化的框架,“细节可控”意味着你的系统更精致.具体体现在应用上.MVC的出现 ...

  10. SQLSERVER语句 in和exists哪个效率高本人测试证明

    SQLSERVR语句 in和exists哪个效率高本人测试证明 最近很多人讨论in和exists哪个效率高,今天就自己测试一下 我使用的是客户的数据库GPOSDB(已经有数据) 环境:SQLSERVE ...