深夜爆肝:万字长文3种语言实现Huffman树(强烈建议三连)

Posted 刘一哥GIS

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深夜爆肝:万字长文3种语言实现Huffman树(强烈建议三连)相关的知识,希望对你有一定的参考价值。

一、C语言能干大事

1. C语言下Huffman树的计算过程分析

例1 有权重集合分别是:5、29、7、8、14、23、3、11,计算Huffman树。

这个题目的计算过程如下:

(1)首先是把数据填写在以下表格里:


这个在编程中一定注意:空白格子里是NULL,这点不要搞错。

首先是寻找到两个最小权重的结点,找到的是第7、1号结点,权重合计是8,我们先标记这两个结点s=1(代表已经处理过了),并生成第9号结点,权重是8,并让第7、1号结点的父结点是9,第9号结点的左、右孩子分别是第7、1结点,就是如下表:


重复上面的过程,从头寻找s=0的结点里、权重最小的两个结点、就是第3、4号结点,权重合计是15,这样,标记这两个结点s=1,并生成第10号结点,权重是15,而第7、8的父结点是第10号结点,第10号结点的左右孩子是第3、4号结点,就是下表:


重复这个过程,处理到第15个结点,使其权重合计为100,就是:


最终这个树的计算到此结束,在这样的表中计算出的过程以及结果,就是我们下面编程的主要依据。

2. C语言下Huffman树的编程

针对前面介绍的表格,用C语言描述这样的表就是:

struct Huffman 
{
	int W,Parent,lChild,rChild,S;
};

对Huffman树,由于它是正则二叉树,所以有n个权重数据则必然有2*n-1个树结点。

有了表的C语言定义,则首先是寻找未标记的、权重最小的结点,如果这个表是H,则全部代码就是:

int FindMinNode(struct Huffman *H,int N)
{
	int i,W,Node;
	W=100;Node=0;
	if(H==NULL) return -1;
	for(i=0;i<N;i++)
	{
		if(H[i].W>0&&H[i].W<W&&H[i].S==0)
		{
			W=H[i].W;Node=i;
		}
	}
	H[Node].S=1;
	return Node;
}

第6至第12行,是一个典型的数组中求最小值的算法,在第13行,则必须标记这个结点的S=1,说明该结点已经使用过,最后则返回这个结点的编号。

有了这个函数后,首先要对H表进行两次求最小值的操作,就是:

min1=FindMinNode(H,N);	min2=FindMinNode(H,N);

如第i行是新增的一个结点,则让该表第i行的权重为:

H[i].W=H[min1].W+H[min2].W;

然后,就是设置这个结点的左右孩子结点为min1、min2,就是:

H[i].Parent=-1; 	H[i].lChild=min1;		H[i].rChild=min2;

最后,就是设置编号min1、min2的结点的父结点是第i个结点。

H[min1].Parent=i;	H[min2].Parent=i;

全部就是:

int ConstructHuffmanTree(struct Huffman *H,int n)
{
	int i;
	int min1,min2,N;
	if(n==0) return -1;
	if(H==NULL) return -2;
	N=2*n-1;
	for(i=n;i<N;i++)
	{
		min1=FindMinNode(H,N);min2=FindMinNode(H,N);
		H[i].W=H[min1].W+H[min2].W;
		H[min1].Parent=i;H[min2].Parent=i;
		H[i].Parent=-1;
		H[i].lChild=min1;H[i].rChild=min2;
	}
	return 0;
}

注意这个函数第8行,它是从第n个结点开始循环的。

有了这两个函数后,用一个测试的main()来测试它们:

main()
{
	int n,N,i,*D;
	struct Huffman *H;
	n=8;//组织scanf()输入
	N=2*n-1;
	D=(int *)malloc(sizeof(int)*n);
	//组织scanf()输入。
	D[0]=5;
	D[1]=29;
	D[2]=7;
	D[3]=8;
	D[4]=14;
	D[5]=23;
	D[6]=3;
	D[7]=11;
	H=(struct Huffman *)malloc(sizeof(struct Huffman)*N);
	for(i=0;i<N;i++) 
	{
		H[i].W=0;
		H[i].Parent=0;
		H[i].lChild=0;
		H[i].rChild=0;
		H[i].S =0;
	}
	for(i=0;i<n;i++)
		H[i].W =D[i];
	ConstructHuffmanTree(H,n);
 	printf("ID\\tW\\tP\\tL\\tR\\n");
	for(i=0;i<N;i++)
	printf("%d\\t%d\\t%d\\t%d\\t%d\\n",i,H[i].W,H[i].Parent,H[i].lChild,H[i].rChild);
}

运行结果如下图所示:

Huffman树的C语言程序到此为止。

二、C#语言也不赖

1. C#下Huffman类的设计

仅仅针对前面C语言中的计算方法,设计一个类来完成计算过程,这个类就是:

 class Huffman
    {
        public int W, pChild, lChild, rChild, s;
        public Huffman()
        {
            W = pChild = lChild = rChild = -1;
            s =0;  
        }
        public Huffman(int Weight, int PChild, int LChild, int RChild, int Select)
        {
            W = Weight; PChild = pChild; lChild = LChild; rChild = RChild; s = Select;    
        }
    }

2. C#中界面设计

有了这个类以后,我们可以在界面设计上拖进两个命令按钮button1,button2,然后再拖进一个treeView1控件和imageList1控件,然后开始以下设置:

(1) 选择imageList1控件,找到属性images,加入文件夹MyIcon中的两个小图标;

(2) 选择treeView1控件,让imageList属性选中imageList1控件;

(3) 选择treeView1控件,让SelectImageIndex=1(打开书的图标);

(4) 选择treeView1控件,让ImageIndex=0(关闭书的图标);

(5) 选择button1控件,修改text属性为:”简单测试”;

(6) 选择button2控件,修改text属性为:”结束”

3. 建立测试数据并显示Huffman树

设有权重数据为:5,29,7,8,14.23.3.11,注意权重数据和必须是100。首先是编写从Huffman类数组中取得最小权重结点的函数,这个函数编写在Form1程序中,鼠标双击button1,注意在button1_click()前面补充这样的函数,就是:


在这个函数中,H是Huffman结点对象数组,而N是这个数组的数据个数,如果是上例,则N=15。
现在开始补充代码如下:

int FindMinNode(Huffman[] H,int N)
        {   
        	int i,W,Node;
	        W=100;Node=0;
	        if(H==null) return -1;
	        for(i=0;i<N;i++)
	        {
		        if(H[i].W>0&&H[i].W<W&&H[i].s==0)
		            {
			        W=H[i].W;Node=i;
		            }
	        }
	        H[Node].s=1;
	        return Node;
        }

这个函数同C的几乎没什么差别,同样返回这个结点的下标。有这个函数后,就可以构造Huffman树,跟随着上面的函数,继续输入就是:

int ConstructHuffmanTree(Huffman[] H,int n)
        {
	        int i;
	        int min1,min2,N;
	        if(n==0) return -1;
	        if(H==null) return -2;
	        N=2*n-1;
	        for(i=n;i<N;i++)
	        {
		        min1=FindMinNode(H,N);
		        min2=FindMinNode(H,N);
		        H[i].W=H[min1].W+H[min2].W;
		        H[min1].pChild=i;
		        H[min2].pChild=i;
		        H[i].pChild=-1;
		        H[i].lChild=min1;
		        H[i].rChild=min2;
	        }
	    return 0;
        }

其基本算法和C语言的也没什么差别。

但这个Huffman类的树是不能显示在treeView1中的,控件treeView1只能显示TreeNode类型的树,所以要按这个表格的内容构造TreeNode类对象的树。

在treeView1控件中,显示的结点个数同Huffman类的结点个数是一致的,而每个TreeNode类结点的Text内容则是权重,于是紧跟着上面的函数,写以下函数就是:

        void dTree(Huffman[] H)
        {
            int i,n,a,b;
            n = H.Count();
            TreeNode[] T = new TreeNode[n];
            for(i=0;i<n;i++)
                T[i]=new TreeNode(H[i].W.ToString());
            for (i = 0; i <n; i++)
            {
                a = H[i].lChild; 
b = H[i].rChild;
                if (a >= 0) T[i].Nodes.Add(T[a]);
                if (b >= 0) T[i].Nodes.Add(T[b]);
            }
            treeView1.Nodes.Add(T[n-1]);  
        }

最后,就是补充button1下的程序,按实验数据有:

这组数据一共8个,所以Humman树一共将有2*8-1=15个结点,鼠标双击button1,写进以下程序:

private void button1_Click(object sender, EventArgs e)
        {
            Huffman[] H = new Huffman[15];
            H[0] = new Huffman(5, -1, -1, -1, 0);
            H[1] = new Huffman(29, -1, -1, -1, 0);
            H[2] = new Huffman(7, -1, -1, -1, 0);
            H[3] = new Huffman(8, -1, -1, -1, 0);
            H[4] = new Huffman(14, -1, -1, -1, 0);
            H[5] = new Huffman(23, -1, -1, -1, 0);
            H[6] = new Huffman(3, -1, -1, -1, 0);
            H[7] = new Huffman(11, -1, -1, -1, 0);
            for (int i = 8; i < 15; i++) H[i] = new Huffman();
            ConstructHuffmanTree(H, 8);
            dTree(H);
        }

到此,简单的Huffman树的测试程序设计完成。最后在button2_click()中补充代码:this.close();让程序能正常结束。

4. 输入任意一组数据,完成构造Huffman树

为完成这个要求,首先要在界面设计中再补充控件,有:

(1) 补充listBox1控件;

(2) 补充button3控件,修改Text属性为:“输入确认”;

(3) 补充button4控件,修改Text属性为:”生成Huffman树”;

(4) 补充button5控件,修改Text属性为:”清除”;

(5) 补充textBox1控件;

有了上述控件后,我们首先设计操作过程是:

在textBox1控件中输入数据,按下button3按钮“输入确认”,则输入的数据显示在listBox1控件中,直到所有数据输入完成,于是这样的操作要求的程序就是:

private void button3_Click(object sender, EventArgs e)
        {
            listBox1.Items.Add(textBox1.Text); 
        }

listBox1控件是个数据容器,你可以不断追加进很多数据,这些数据全部可以保存在这个控件中。直到按下button4”生成Huffman树”,才开始计算。所以button4的程序就是:

  private void button4_Click(object sender, EventArgs e)
        {
            int n = listBox1.Items.Count;
            Huffman[] H = new Huffman[2*n-1];
            for (int i = 0; i < 2 * n - 1; i++)
                H[i] = new Huffman();
            for(int i=0;i<n;i++)
            {
                int w=int.Parse( listBox1.Items[i].ToString()) ;
                H[i].W = w; 
            }
            ConstructHuffmanTree(H, n);
            dTree(H);
        }

表7中第3行,相当于从listBox1控件中获得数据项的个数,在第9行,就是逐个取得每个数据项,并转换成int类型、赋值给Huffman类对象数组中每个对象的权重W。最后按同样的方式计算并显示在treeView1中。

注意这个程序实际很不理想,没判断输入的数据是否有负值、是否是非数字的字符串、是否和为100等等,这些详细的判断留给同学们自己去完成。

对于button5”清除”的编程非常简单,就是:

private void button5_Click(object sender, EventArgs e)
{
     listBox1.Items.Clear();
     treeView1.Nodes.Clear();
     textBox1.Text = "";
}	

程序运行效果:

三、JavaScript语言不爱听了

1. JavaScript下Huffman类的设计

针对前面C语言中的计算方法,设计一个类来存储数据,如果你使用的Ext系统,则强烈建议直接使用Ext.data.ArrayStore类对象直接构造它,这样的好处是显示在表格里非常方便,于是说明对象tstore为:

var tstore = new Ext.data.ArrayStore({
		data:[
			[0,5 ,-1, -1,-1,0],
			[1,29,-1, -1,-1,0],
			[2,7 ,-1, -1,-1,0],
			[3,8 ,-1, -1,-1,0以上是关于深夜爆肝:万字长文3种语言实现Huffman树(强烈建议三连)的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript之爆肝汇总万字长文❤值得收藏

坚持原创 绝不注水爆肝万字长文Java 语言的基本特性(喂饭式教程)

与学妹深夜互动,熬夜肝出Java类万字长文,才满足于她...

ChatGPT为啥这么强:万字长文详解 by WolframAlpha之父

[保姆级万字教程]打造最迷人的S曲线----带你从零手撕基于Huffman编码的文件压缩项目

[保姆级万字教程]打造最迷人的S曲线----带你从零手撕基于Huffman编码的文件压缩项目