霍夫曼编码原理 C#版本

来源:互联网 发布:淘宝店铺怎么推广好 编辑:程序博客网 时间:2024/06/05 04:54

               霍夫曼编码原理

在数据通信时,可以用01码的不同排列来表示字符。例如给定一段报文CAST CAST SAT AT A TASA,在报文中出现的字符集合是{C,A,S,T},各个字符出现的频度是{2,7,4,5}。若给每个字符一个等长的二进制表示,例如 C:00 A:01 S:10 T:11,则所发的报文将是00011011 00011011 100111 0111 01 11011001,共计(2+7+5+4)*2=36个码。若按字符出现的频度不同给予不同长度的编码,出现频度较大的字符采用为数较少的编码,出现频度较小的字符采用位书较多的编码,可以是报文的码数降到最小,这就是所谓的最小冗余编码问题。霍夫曼编码就能实现这种最小冗余编码。上例中按字符出现的频度进行编码,A:0 T:10 S:110C:111,则最终的报文只有35个码,节省了传输中使用的单元。因而霍夫曼编码是一种被广泛应用而且非常有效的数据压缩技术。

二.编码实现

一般情况下,霍夫曼编码的工作主要分为三步。

第一步是准备工作,对于需要编码的字符(一般存在于文件里)进行扫描,统计每个字符出现的频次,得到一个整数数组。

第二步根据这个频次数组构造一棵霍夫曼树,这步是霍夫曼编码的核心内容。

第三步,再次扫描一遍待编码的字符,对每个字符,在霍夫曼树里搜索该字符,得到它的编码。

这里通过C#软件实现,便于可视化的界面及操作方便性

// 学习小结 吴新强于2013年3月20日16:58:11 桂电 2507
//
//
//


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections;


namespace HuffmanCoding
{
    public partial class Form1 : Form
    {


        public Form1()
        {
            InitializeComponent();
        }
        
        public struct Node//定义节点结构
        {
            public int weight; //定义权重
            public int parent, lchild, rchild;//定义父亲,孩子 
        };
        public struct Min//定义最小两位数结构
        {
            public int s1;
            public int s2;
        };
        Node[] HTN = new Node[500];
        Min Getmin(int n) //寻找最小的两位权值
        {
            int min1, min2, i;
            Min code; //定义结构体Min
            code.s1 = 1;
            code.s2 = 1;
            min1 = 256;
            min2 = 256;
            for (i = 0; i < n; i++)//寻找最小值 
            {
                if (HTN[i].weight <= min1 && HTN[i].parent == 0)
                {
                    min1 = HTN[i].weight;
                    code.s1 = i;


                }
            }


            for (i = 0; i <= n; i++)//寻找次最小值 
            {
                if (HTN[i].weight <= min2 && i != code.s1 && HTN[i].parent == 0)
                {
                    min2 = HTN[i].weight;
                    code.s2 = i;
                }
            }
            return code;
        }
        private void Form1_Load(object sender, EventArgs e)
        {


        }
        private void label1_Click(object sender, EventArgs e)
        {


        }
        private void richTextBox1_TextChanged(object sender, EventArgs e)
        {


        }
        private void richTextBox2_TextChanged(object sender, EventArgs e)
        {


        }
        private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
        {


        }
        private void button1_Click(object sender, EventArgs e)//编码响应
        {
            int length;//统计输入的字符个数
            int Num = 0;//统计输入字符的种类
            int i, j, k, m, start, c, f;
            Min min;
            int[] weight = new int[256];//用于统计每次字符出现的次数
            int[] ind = new int[256];//用于统计i对应的字符
            int[] wei = new int[256];//用于统计每个字符权重
            string Input = richTextBox1.Text.Trim();//输入的编码字符存到Input数组中
            string code = "";
            if (richTextBox1.Text == "") MessageBox.Show("请输入编码字符!");
            else
            {
                length = richTextBox1.Text.Length;
                for (i = 0; i < length; i++)
                    weight[Input[i]]++; //统计每个字符出现的次数即权重
                for (i = 0; i < 256; i++)
                {
                    if (weight[i] != 0) //若个数不为0,将这个字符存入ind数组中,
                    {
                        ind[Num] = i; //Ind存放字符代码 
                        wei[Num++] = weight[i];//相应的权重存放在weight数组中,记录个数输入的字符有num种
                    }
                }


                m = 2 * Num - 1;
                for (i = 0; i < Num; i++)//初始化
                {
                    HTN[i].weight = wei[i];
                    HTN[i].parent = 0;
                    HTN[i].lchild = 0;
                    HTN[i].rchild = 0;
                }
                for (; i < m; i++)
                {
                    HTN[i].weight = 0;
                    HTN[i].parent = 0;
                    HTN[i].lchild = 0;
                    HTN[i].rchild = 0;
                }


                for (i = Num; i < m; i++)//建树
                {
                    min = Getmin(i - 1); //找出最小的两位数
                    HTN[min.s1].parent = i;
                    HTN[min.s2].parent = i;
                    HTN[i].lchild = min.s1;
                    HTN[i].rchild = min.s2;
                    HTN[i].weight = HTN[min.s1].weight + HTN[min.s2].weight;
                }
                string[] HC = new string[Num];
                char[] cd = new char[Num + 5];
                for (i = 0; i < Num; i++)//对每个字符进行编码
                {
                    start = Num + 5;
                    for (c = i, f = HTN[i].parent; f != 0; c = f, f = HTN[f].parent)
                    {
                        if (HTN[f].lchild == c) cd[--start] = '0';
                        else cd[--start] = '1';
                    }
                    for (j = start; j < Num + 5; j++)
                        HC[i] += cd[j];
                }


                for (i = 0; i < length; i++)//将输入字符编程码
                {
                    k = 0;
                    int flag = 0;
                    for (j = 0; j < Num && flag == 0; j++)
                    {
                        if (ind[j] == Input[i])//寻找该字符对应的编码
                        {
                            k = j;
                            flag = 1;
                        }


                    }
                    code += HC[k].ToString();
                }


                richTextBox2.Text = code;//最终编码输出至richTextBox2中
                for (i = 0; i < Num; i++)//将每个字符编码输出至listbox中
                {
                    this.listBox1.Items.Add(Convert.ToString(Convert.ToChar(ind[i])) + ":  " + HC[i]);


                }
            }
        }
        private void button2_Click(object sender, EventArgs e)//重新输入响应
        {


            richTextBox1.Text = "";
            richTextBox2.Text = "";
            this.listBox1.Items.Clear();
        }
        private void button3_Click(object sender, EventArgs e)//退出响应
        {
            this.Close();
        }
    }
}

实验截图:


原创粉丝点击