技术图文:如何利用C#实现Huffman编码?
背景
Huffman编码 在通信和数据压缩领域具有重要的应用。
在介绍 Huffman 编码具体实现之前,先介绍几个相关的概念。
概念1:树中结点的带权路径长度 – 根结点到该结点的路径长度与该结点权值的乘积。
概念2:树的带权路径长度 – 树中所有叶子结点的带权路径长度之和。
概念3:huffman树 – n个带权叶子结点构成的所有二叉树中,带权路径长度最小的二叉树。
概念4:前缀码规则 – 对字符集编码时,字符集中任一字符的编码都不是其它字符编码的前缀。
概念5:哈夫曼编码 – 将哈夫曼树中,每个分支结点的左分支标0,右分支标1,把从根结点到每个叶子结点的路径上的标号连接起来作为该叶子结点所代表符号的编码,这样得到的编码称为Huffman编码。
huffman编码 是满足前缀码规则的编码方案,对利用 huffman编码 的字符集,进行解码时不存在二义性。
技术分析
要对一个字符集合,例如“state,seat,act,tea,cat,set,a,eat”进行编码。
首先,要对每个字符出现的频数进行统计,根据统计的结果构造 Huffman 树。
- 字符
c
,出现的频数为2。 - 字符
s
,出现的频数为3。 - 字符
e
,出现的频数为5。 - 字符
a
,出现的频数为7。 - 字符
,
,出现的频数为7。 - 字符
t
,出现的频数为8。
构造 Huffman 树的算法如下:
第1步:根据给定的 n 个权值 w1,w2,……,wn,构成 n 棵二叉树的森林 F={T1,T2,……,Tn} ,其中每棵二叉树 Ti 中都只有一个权值为 wi 的根结点,其左右子树均为空(对应代码实现的Step3)。
第2步:在森林 F 中选出两棵根结点权值最小的树作为一棵新树的左右子树,且置新树的根结点的权值为其左右子树上根结点的权值之和。
第3步:从 F 中删除构成新树的哪两棵树,同时把新树加入 F 中。
第4步:重复第2和3步,直到 F 中只含有一棵树为止,此树便是Huffman树(对应代码实现的Step4)。
该算法为典型的贪心算法,以上字符集经过处理后得到的 Huffman树,如下所示:
其次,利用 Huffman 树对字符集中的每个字符进行编码,得到字符与编码对应的字典(对应代码实现的Step5)。
- 字符
c
,对应的编码1110
。 - 字符
s
,对应的编码1111
。 - 字符
e
,对应的编码110
。 - 字符
a
,对应的编码00
。 - 字符
,
,对应的编码01
。 - 字符
t
,对应的编码10
。
最后,利用字典对数据进行编码和解码(对应代码实现的Step6、Step7)。
对数据 state,seat,act,tea,cat,set,a,eat
进行编码,得到的结果如下:
1111100010110011111110001001001110100110110000111100010011111110100100011100010
当然,由于 Huffman 编码满足前缀码规则,解码之后仍然为:state,seat,act,tea,cat,set,a,eat
,具有唯一性。
代码实现
Step1:构造 Huffman 树结点的结构 HuffmanTreeNode
。
封装二叉树结点的结构
public class BinTreeNode<T> : where T : IComparable<T>
{private T _data;/// <summary>/// 获取或设置该结点的左孩子/// </summary>public BinTreeNode<T> LeftChild { get; set; }/// <summary>/// 获取或设置该结点的右孩子/// </summary>public BinTreeNode<T> RightChild { get; set; }/// <summary>/// 获取或设置该结点的数据元素/// </summary>public T Data{get { return _data; }set{if (value == null)throw new ArgumentNullException();_data = value;}}/// <summary>/// 初始化BinTreeNode类的新实例/// </summary>/// <param name="data">该结点的数据元素 </param>/// <param name="lChild">该结点的左孩子</param>/// <param name="rChild">该结点的右孩子</param>public BinTreeNode(T data, BinTreeNode<T> lChild = null, BinTreeNode<T> rChild = null){if (data == null)throw new ArgumentNullException();LeftChild = lChild;_data = data;RightChild = rChild;}
}
封装 Huffman 树结点的结构,该结点为二叉树节点的子类。
public class HuffmanTreeNode : BinTreeNode<char>
{/// <summary>/// 结点的权值/// </summary>public int Weight { get; set; }/// <summary>/// 叶子结点 -- 字符/// </summary>/// <param name="data"></param>/// <param name="weight"></param>public HuffmanTreeNode(char data, int weight) : base(data){Weight = weight;}/// <summary>/// 其它结点/// </summary>/// <param name="weight"></param>public HuffmanTreeNode(int weight):base('\0'){Weight = weight;}
}
Step2:封装编码字典的结构HuffmanDicItem
。
public class HuffmanDicItem
{/// <summary>/// 字符/// </summary>public char Character { get; set; }/// <summary>/// 编码/// </summary>public string Code { get; set; }/// <summary>/// 构造函数/// </summary>/// <param name="charactor"></param>/// <param name="code"></param>public HuffmanDicItem(char charactor, string code){Character = charactor;Code = code;}}
Step3:对字符集中的字符进行统计的函数,即构造最初始的森林F
。
private List<HuffmanTreeNode> CreateInitForest(string str)
{if (string.IsNullOrEmpty(str))throw new ArgumentNullException();List<HuffmanTreeNode> result = new List<HuffmanTreeNode>();char[] charArray = str.ToCharArray();List<IGrouping<char, char>> lst = charArray.GroupBy(a => a).ToList();foreach (IGrouping<char, char> g in lst){char data = g.Key;int weight = g.ToList().Count;HuffmanTreeNode node = new HuffmanTreeNode(data, weight);result.Add(node);}return result;
}
Step4:构造Huffman树的函数。
private HuffmanTreeNode CreateHuffmanTree(List<HuffmanTreeNode> sources)
{if (sources == null)throw new ArgumentNullException();if (sources.Count < 2)throw new ArgumentException("构造Huffman树,最少为2个结点。");HuffmanTreeNode root = default(HuffmanTreeNode);bool isNext = true;while (isNext){List<HuffmanTreeNode> lst = sources.OrderBy(a => a.Weight).ToList();HuffmanTreeNode n1 = lst[0];HuffmanTreeNode n2 = lst[1];int weight = n1.Weight + n2.Weight;HuffmanTreeNode node = new HuffmanTreeNode(weight);node.LeftChild = n1;node.RightChild = n2;if (lst.Count == 2){root = node;isNext = false;}else{sources = lst.GetRange(2, lst.Count - 2);sources.Add(node);}}return root;
}
Step5:构造Huffman编码字典的函数。
private List<HuffmanDicItem> CreateHuffmanDict(string code, HuffmanTreeNode current)
{if (current == null)throw new ArgumentNullException();List<HuffmanDicItem> result = new List<HuffmanDicItem>();if (current.LeftChild == null && current.RightChild == null){result.Add(new HuffmanDicItem(current.Data, code));}else{List<HuffmanDicItem> dictL = CreateHuffmanDict(code + "0",(HuffmanTreeNode) current.LeftChild);List<HuffmanDicItem> dictR = CreateHuffmanDict(code + "1",(HuffmanTreeNode) current.RightChild);result.AddRange(dictL);result.AddRange(dictR);}return result;
}private List<HuffmanDicItem> CreateHuffmanDict(HuffmanTreeNode root)
{if (root == null)throw new ArgumentNullException();return CreateHuffmanDict(string.Empty, root);
}
Step6:对字符串进行编码的函数。
private string ToHuffmanCode(string source, List<HuffmanDicItem> lst)
{if (string.IsNullOrEmpty(source))throw new ArgumentNullException();if (lst == null)throw new ArgumentNullException();string result = string.Empty;for (int i = 0; i < source.Length; i++){result += lst.Single(a => a.Character == source[i]).Code;}return result;
}// 被外界调用的函数,对字符串进行huffman编码。
public string StringToHuffmanCode(out List<HuffmanDicItem> dict, string str)
{List<HuffmanTreeNode> forest = CreateInitForest(str);HuffmanTreeNode root = CreateHuffmanTree(forest);dict = CreateHuffmanDict(root);string result = ToHuffmanCode(str, dict);return result;
}
Step7:对编码后的字符串进行解码的函数。
public string HuffmanCodeToString(List<HuffmanDicItem> dict, string code)
{string result = string.Empty;for (int i = 0; i < code.Length;){foreach (HuffmanDicItem item in dict){if (code[i] == item.Code[0] && item.Code.Length + i <= code.Length){string temp = code.Substring(i, item.Code.Length);if (temp == item.Code){result += item.Character;i += item.Code.Length;break;}}}}return result;
}
总结
我们把 Step3至Step7 的代码封装在HuffmanTree
的结构中。通过调用该结构提供的两个 public 方法StringToHuffmanCode
和HuffmanCodeToString
就可以实现对给定字符集的 huffman 编码与解码的操作。
static void Main(string[] args)
{string str = "state,seat,act,tea,cat,set,a,eat";Console.WriteLine(str);HuffmanTree huffmanTree = new HuffmanTree();List<HuffmanDicItem> dic;string code = huffmanTree.StringToHuffmanCode(out dic, str);for (int i = 0; i < dic.Count; i++){Console.WriteLine(dic[i].Character + ":" + dic[i].Code);}Console.WriteLine(code);string decode = huffmanTree.HuffmanCodeToString(dic, code);Console.WriteLine(decode);Console.ReadLine();
}
运行的结果如下所示:
好了,有关 Huffman编码就给大家介绍完了,该编码是二叉树的重要应用之一,希望对学习数据结构与算法的同学有所帮助。See You!
相关图文:
- 如何利用 C# 实现 K 最邻近算法?
- 如何利用 C# 实现 K-D Tree 结构?
- 如何利用 C# + KDTree 实现 K 最邻近算法?
- 如何利用 C# 对神经网络模型进行抽象?
- 如何利用 C# 实现神经网络的感知器模型?
- 如何利用 C# 实现 Delta 学习规则?
- 如何利用 C# 开发「桌面版百度翻译」软件!
- 如何利用 C# 开发「股票数据分析软件」(上)
- 如何利用 C# 开发「股票数据分析软件」(中)
- 如何利用 C# 开发「股票数据分析软件」(下)
- 如何利用 C# 爬取「财报说」中的股票数据?
- 如何利用 C# 爬取 One 持有者返利数据!
- 如何利用 C# 爬取Gate.io交易所的公告!
- 如何利用 C# 爬取BigOne交易所的公告!
- 如何利用 C# 爬取 ONE 的交易数据?
- 如何利用 C# 爬取「京东 - 计算机与互联网图书销量榜」!
- 如何利用 C# 爬取「当当 - 计算机与互联网图书销量榜」!
- 如何利用 C# 爬取「互动出版网 - 计算机图书销量榜」!
- 如何利用 C# 爬取「中国图书网 - 计算机与互联网图书销量榜」!
- 如何利用 C# 爬取「猫眼电影:热映口碑榜」及对应影片信息!
- 如何利用 C# 爬取「猫眼电影专业版:票房」数据!
- 如何利用 C# 爬取「猫眼电影:最受期待榜」及对应影片信息!
- 如何利用 C# 爬取「猫眼电影:国内票房榜」及对应影片信息!
- 如何利用 C# + Python 破解猫眼电影的反爬虫机制?
- 如何利用 C# 爬取带 Token 验证的网站数据?
- 如何利用 C# 向 Access 数据库插入大量数据?
相关文章:

ELK 5.x日志分析 (二) Elasticserach 5.2 安装
2019独角兽企业重金招聘Python工程师标准>>> 解压安装包到/opt/elasticsearch 目录下面 [roots1-prod-it-web01 opt]# tree -L 1 elasticsearch/ elasticsearch/ ├── bin ├── config ├── lib ├── LICENSE.txt ├── modules ├── NOTICE.txt ├── …

什么样的人适合学习UI?
UI时代的到来,让我们的生活都多姿多彩,很多企业越来越注重UI设计这方面,想要学习UI设计的人也越来越多,暗恶魔什么样的人适合学习UI呢? 什么样的人适合学习UI? 目前的UI设计很多都是停留在手机端设计,网页࿰…

Uva 10074【递推dp】
UVa 10074 题意:求01矩阵的最大子0矩阵。 http://www.csie.ntnu.edu.tw/~u91029/MaximumSubarray.html#2 这里说的很清楚。先求Largest Empty Interval,枚举每个点为矩形的右下角。 1 #include<iostream>2 #include<cstdio>3 #include<cstring>4 #include<…

金融时报:谷歌撤离中国有99.9%的可能性
据国外媒体报道,英国《金融时报》周六发表文章称,谷歌与中国政府就监管问题的谈判显然陷入僵局,而这家世界最大的搜索引擎关闭中国业务现在有99.9%的可能性。《金融时报》称,谷歌已经制定了关闭中国搜索引擎的详细计划。该报援引一…

技术图文:匿名方法是怎样演变为Lambda表达试的?
背景 “Lambda 表达式”(lambda expression)是一个匿名函数,Lambda 表达式基于数学中的 λ演算得名,直接对应于其中的 lambda 抽象(lambda abstraction),是一个匿名函数,即没有函数…

python和c++的相互调用教程
日常工作中会遇到需要python与cpp代码之间的相互调用,工作的应用复杂,都是取决于代码的多少,但是总的方法不变,这里用两个简单例子说明下,有兴趣的筒子可以探讨下~~ 我的测试环境:ubuntu1604,py…

技术图文:如何通过 LINQ 查找集合中的重复数据?
背景 在前几天介绍的 如何利用C#实现Huffman编码? 的图文中有以下代码。 private List<HuffmanTreeNode> CreateInitForest(string str) {if (string.IsNullOrEmpty(str))throw new ArgumentNullException();List<HuffmanTreeNode> result new List&…

mysql的基本知识
安装:http://www.cnblogs.com/sshoub/p/4321640.html 导库 http://www.cnblogs.com/yuwensong/p/3955834.html 报错:Error was: No module named PIL pip install image转载于:https://www.cnblogs.com/baldermurphy/p/7403778.html

msys下产生dll的导入库
有些时候在只有一个dll的情况下,如果需要隐式链接的话,就需要为该dll产生一个导入库.注意导入库是不能跨编译器使用的,在mingw中导入库需要以.a结尾,而vs则以.lib 以下的方法是在Msys产生mingw及vs 的导入库 , 打开MSys工具 首先生成dll库的d…

零基础小白如何学习好UI设计
智能时代的来临,很多企业都越来越注重用户体验这一块,想要有一个吸引用户的好页面,UI设计师岗位不可或缺,如今越来越多的人想要学习UI设计技术,那么对于零基础小白如何学习好UI设计呢? 零基础小白如何学习好UI设计? …

技术图文:如何利用BigOne的API制作自动化交易系统 -- 获取账户资产
背景 前几天我们介绍了如何使用 BigONE Developer API V2 来获取身份令牌的方法「如何利用BigOne的API制作自动化交易系统 – 身份验证」。一旦获取了身份令牌,我们就可以在网络请求的 header 中加入令牌来获取自己的账户数据,创建买入、卖出订单&#…

『网站升级』PHPWind8.0至8.3升级过程及问题种种回顾录
上星期的PHPWind杭州峰会之后,PHPWind发布了8.3版。紧接着淘连接,淘满意,团购PHPWind的一系统ARP应用开始进入我们公司技术苦力的耳朵里(也就是偶),偶知道有大事要发生了。于是乎。领导悠然降至,…

新浪 抓取详情页
转载于:https://www.cnblogs.com/tian-sun/p/7404493.html

零基础如何学习软件测试
很多人想学软件测试是因为软件测试是进入到IT行业里比较快的一门技术,软件测试的门槛比较低,初学者和零基础小白学起来都是比较容易的,下面小编就详细的给大家介绍一下具体零基础如何学习软件测试? 零基础如何学习软件测试?对于初级测试而言…
VPS使用初体验
很早就想建个人网站,但是出于各种限制,一直没有实施。前几天开通了网银,便再次萌发了建站的想法。。。 购买一个了enom的域名,然后寻找比较好的虚拟主机,发现ubuntuchina上有个卖vps的,价格还行,…

LeetCode实战:相同的树
题目英文 Given two binary trees, write a function to check if they are the same or not. Two binary trees are considered the same if they are structurally identical and the nodes have the same value. Example 1: Input: 1 1/ \ / \2 3 …

mysql数据库常用命令
登录: mysql -h 服务器地址 -u 登录名 -P 端口 -p 密码 (登录时最好先不输入密码,等下一条提示出来之后再输,这样可以在界面中隐藏密码) 退出: quit 或者 exit 注意:登录数据库系统后࿰…

Java入门学习注意事项有哪些?
想要学好java技术,做好学习规划路线和注意事项是非常重要的,尤其是零基础学员,Java涉及到的知识点非常多,我们需要制定合理的Java学习路线图,这样会事半功倍,下面小编和大家总结一下Java入门学习注意事项有…

在Hibernate中处理批量更新和批量删除
批量更新是指在一个事务中更新大批量数据,批量删除是指在一个事务中删除大批量数据。以下程序直接通过Hibernate API批量更新CUSTOMERS表中年龄大于零的所有记录的AGE字段: 如果CUSTOMERS表中有1万条年龄大于零的记录,那么Session的find()方法…

LeetCode实战:对称二叉树
题目英文 Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center). For example, this binary tree [1,2,2,3,4,4,3] is symmetric: 1/ \2 2/ \ / \ 3 4 4 3But the following [1,2,2,null,3,null,3] is not: 1/ \2 2\ \3 …

flannel 概述 - 每天5分钟玩转 Docker 容器技术(58)
2019独角兽企业重金招聘Python工程师标准>>> flannel 是 CoreOS 开发的容器网络解决方案。flannel 为每个 host 分配一个 subnet,容器从此 subnet 中分配 IP,这些 IP 可以在 host 间路由,容器间无需 NAT 和 port mapping 就可以跨…

Python如何实现穷举搜索?
穷举搜索就是在整个搜索空间范围内尝试每一种可能性,直到找到目标值或者整个搜索空间都找完也没有找到目标值。最常见的穷举搜索就是线性搜索,即按照顺序简单检查所有不同的可能性。 例如:2个警察追逐强盗到了一个废弃旅馆的二楼走廊…

技术图文:如何利用BigOne的API制作自动化交易系统 -- 订单系统
背景 前面几天,我们一起封装了 BigONE 提供的“身份验证”与“资产账户”部分的 API。 如何利用BigOne的API制作自动化交易系统 – 身份验证如何利用BigOne的API制作自动化交易系统 – 获取账户资产 现在,离搭建咱们的自动化交易系统更近一步了。 本…

[解决]eclipse中android自动补全/提示卡机或假死
这是Eclipse3.6版本的特有问题,想彻底解决此问题的话,还是建议换为3.5/3.4; 在保持版本不变的前提下,可以按如下方法优化下: 解决办法:1. 找到你的JDK安装目录下的src.zip文件;2. 打开eclipse: …

17.SpringMVC核心技术-拦截器
SpringMVC 中的 Interceptor 拦截器是非常重要和相当有用的,它的主要作用是拦截指定 的用户请求, 并进行相应的预处理与后处理。其拦截的时间点在“处理器映射器根据用户提 交的请求映射出了所要执行的处理器类, 并且也找到了要执行该处理器类…

Python培训讲解二叉树的三种深度
python代码实现了二叉树,这次将会实现二叉树的几种遍历方法,来更好的解析二叉树的结构特点。分别是一种广度遍历,和三种深度遍历方法:先序遍历,中序遍历,后序遍历。下面是代码实现: 1、先序遍历…

Ajax弹出漂亮可拖动的提示层(窗)效果
<html><head><meta http-equiv"Content-Type" content"text/html; charsetgb2312" /><title>Ajax提示框</title><style type"text/css">a{ color:#000; font-size:12px;text-decoration:none}a:hover{ colo…

技术图文:如何解决 DAO 抛出的 80040154 错误?
背景 前几天,咱们一起解决了向 Access 数据库插入大量数据效率底下的问题。通过实验表明:利用 DAO 的方式可以极大的提升数据插入速度。 如何利用 C# 向 Access 数据库插入大量数据? 可是,给电力局升级了软件产品之后ÿ…

路由器配置实践 教你如何在Linux中三台主机两个网段互相通信
大家好我是你们的齐天大圣又到了齐天大圣给大家讲解的时间了今天我带你们做一个 大大项目 你们信不信如果把你不小心打开这个文档 希望你能看完 这个博文花费了我两天的时间所以请尊重我的劳动 假装看完好吗 齐天大圣在此谢过各位看官首先欢迎大家观看操作步骤我们正式开始题目…

Web前端学习有哪些技巧?
想要学好web前端技术,在学习过程中找到合适的方法和技巧,那么在实际学习过程中会更加的容易和快速掌握知识重点,尤其是对于初学者尤为关键,下面小编就为大家详细的介绍一下Web前端学习有哪些技巧?希望能够对学习有所帮助。 Web前…