Huffman编码

来源:互联网 发布:知乎 宋雨珊 编辑:程序博客网 时间:2024/04/19 16:22

未来一段时间我要整理一下数据结构相关的一些东西。包括树,图等等。今天就先从树开始,这次登场的是Huffman树。

Huffman编码的目的是,最小化编码的长度,用最小化的编码量,还表达数据信息。比如我们有三个字母要进行编码,分别是a,b,c.。可以分别编码为01,11,10,也可以编码为011,010,001。反之只要能区分出来就可以。但这都不是最小化的编码。

其中一种最小化的编码是:0,10,11。再考虑到三个字母的出现频率,分别对应上0,10和11,那么传输同样的信息,用Huffman编码就可以尽量减少传递的数据量,但是信息量一点都没有减少。

我用的实现方法很简单。就是先把要进行编码的数据都保存起来,放到一个Huffman树的队列里面,队列里面的每个数据都是树上的一个节点,保存了节点的编码和权重。如果有N个数据,那队列里面就有N个节点,也可以说有N棵树,当然了,还可以说有N棵Huffman树。

然后从这些树(或者说节点)里面,找到权最小的两个,按照左小右大的原则,组合成一个新的二叉树。这个新的二叉树,左右两个子树,就是刚刚取出的两棵小Huffman树。而它的权,则是左右两个子树的权的和。

现在,这个新的二叉树,就是一棵小小的Huffman树,把它放回到队列里面,然后重复上面的步骤,继续找出全重最小的两个,然后组合成一个新的Huffman树,再放回队列……直到队列里面只有一棵树。这最后的一棵树,就是我要实现的Huffman树了。

现在还剩下一件事情,就是从树里把Huffman编码提取出来。提取的时候,左为0,右为1,编码按照从上到下的顺序进行组合。
比如下面的树:
    root
   /  /
    *       c
  / / 
     a   b
Huffman编码分别为:
a:00
b:01
c:1

使用Huffman编码的时候就更方便了。比如我们要发送abc,只要发送00011就可以了,每个编码直接都不需要用分割符号。因为解析的时候,诸位提取,对照Huffman编码进行解析就可以了。比如先读到0,编码表里没有0,就继续。然后又读到0,这个时候就有了一个匹配a,然后继续,就又得到了b和c。

下面是编码的具体实现:
类BaseNode定义了基本的节点:
public class BaseNode
{
 //左节点
 public BaseNode Left = null;
 //右节点
 public BaseNode Right = null;
 //权
 public int Number = 0;
}

类HuffNode定义了Huffman树的节点
public class HuffNode : BaseNode
{
 //数据
 public object Obj = null;
 //Huffman编码
 public string Code;
 public HuffNode()
 { }

 public HuffNode(int number, object obj)
 {
  Number = number;
  Obj = obj;
 }
}

下面是Huffman树的代码:
public class Huffman
{
 //mylist作为Huffman树队列
 private List<HuffNode> mylist = new List<HuffNode>();
 
 //定义了一个事件,用来处理生成的Huffman编码
 public event EventHandler<NodeEventArgs> OnNodeFound;

 //向队列里面添加节点
 public void Add(HuffNode value)
 {
  mylist.Add(value);
 }

 //找到队列里面最小的Huffman树,返回它,同时从队列里面去掉
 private HuffNode min()
 {
  HuffNode anode;
  if (mylist.Count > 0)
  {
   anode = mylist[0];
   foreach (HuffNode tempnode in mylist)
   {
    if (anode.Number > tempnode.Number)
    {
     anode = tempnode;
    }
   }
   mylist.Remove(anode);
   return anode;
  }
  else
   return null;
 }

 //生成Huffman树
 public void CreateHuffmanTree()
 {
  HuffNode node = null;
  while (mylist.Count > 1)
  {
   HuffNode tempnode = new HuffNode();
   //取得两个最小的Huffman子树
   tempnode.Left = min();
   tempnode.Right = min();
   tempnode.Number = tempnode.Left.Number + tempnode.Right.Number;
   mylist.Add(tempnode);
  }
  if (mylist.Count > 0)
   node = mylist[0];
  //创建Huffman编码
  if (node != null)
  {
   StringBuilder sb = new StringBuilder();
   Stack<HuffNode> stack = new Stack<HuffNode>();
   //指向树根
   HuffNode pNow = node;
   while (null != pNow)
   {
    //如果有左子树,就进入
    if (null != pNow.Left)
    {
     //保存当前节点
     stack.Push(pNow);
     //进入左子树
     sb.Append(0);
     pNow = pNow.Left as HuffNode;
     pNow.Code = sb.ToString();
    }
    else
    {
     //没有左子树,判断右子树
     if (null != pNow.Right)
     {
      //进入右子树
      sb.Append(1);
      pNow = pNow.Right as HuffNode;
      pNow.Code = sb.ToString();
     }
     else
     {
      //是叶子,响应自定义事件,输出Huffman编码
      NodeEventArgs args = new NodeEventArgs();
      args.Node = pNow;
      if (OnNodeFound != null)
       OnNodeFound(this, args);
      //如果堆栈为空,就退出
      if (stack.Count == 0)
       break;
      else
      {
       pNow = stack.Pop();
       sb.Remove(0, sb.Length);
       sb.Append(pNow.Code);
       //进入这个节点的右子树
       pNow = pNow.Right as HuffNode;
       sb.Append(1);
       pNow.Code = sb.ToString();
       //如果右子树为空,并且堆栈不空,就继续
       while (null == pNow && stack.Count != 0)
       {
        pNow = stack.Pop();
        pNow = pNow.Right as HuffNode;
       }
       sb.Remove(0, sb.Length);
       if (pNow != null)
        sb.Append(pNow.Code);
      }
     }
    }
   }
  }
 }
}

自定义事件参数NodeEventArgs的定义
public class NodeEventArgs : EventArgs
{
 public BaseNode Node;
}

测试和使用:
private Huffman huffman = new Huffman();
       
private void btnHuffman_Click(object sender, EventArgs e)
{
 //添加节点
 huffman.Add(new HuffNode(120, 'e'));
 huffman.Add(new HuffNode(90, 't'));
 huffman.Add(new HuffNode(80, 'a'));
 huffman.Add(new HuffNode(80, 'i'));
 huffman.Add(new HuffNode(80, 'n'));
 huffman.Add(new HuffNode(80, 'o'));
 huffman.Add(new HuffNode(80, 's'));
 huffman.Add(new HuffNode(64, 'h'));
 huffman.Add(new HuffNode(62, 'r'));
 huffman.Add(new HuffNode(44, 'd'));
 huffman.Add(new HuffNode(40, 'l'));
 huffman.Add(new HuffNode(34, 'u'));
 huffman.Add(new HuffNode(30, 'c'));
 huffman.Add(new HuffNode(30, 'm'));
 huffman.Add(new HuffNode(25, 'f'));
 huffman.Add(new HuffNode(20, 'w'));
 huffman.Add(new HuffNode(20, 'y'));
 huffman.Add(new HuffNode(17, 'g'));
 huffman.Add(new HuffNode(17, 'p'));
 huffman.Add(new HuffNode(16, 'b'));
 huffman.Add(new HuffNode(12, 'v'));
 huffman.Add(new HuffNode(8, 'k'));
 huffman.Add(new HuffNode(5, 'q'));
 huffman.Add(new HuffNode(4, 'j'));
 huffman.Add(new HuffNode(4, 'x'));
 huffman.Add(new HuffNode(2, 'z'));
 //注册事件,当生成一个Huffman编码的时候,就输出编码值
 huffman.OnNodeFound += new EventHandler<NodeEventArgs>(huffman_OnNodeFound);
 //生成Huffman树
 huffman.CreateHuffmanTree();
}

void huffman_OnNodeFound(object sender, NodeEventArgs e)
{
 //输出Huffman编码
 HuffNode node = e.Node as HuffNode;
 //如果Obj为空,说明这个节点没有Huffman编码,不进行处理
 if (node.Obj != null)
  System.Console.WriteLine("char:{0}  code:{1}",node.Obj.ToString(),node.Code);
}

 

原创粉丝点击