哈夫曼编码(c/c++/c#/php/js/java 版)
简介................................................................................................................................. 1
算例................................................................................................................................. 1
一 、动态构建树及树的存储............................................................................................ 1
1.1 算例转化为伪代码............................................................................................... 2
1.2 树结构中的临时存储结构.................................................................................... 3
二、c、c++、c#、php、js、java 不同的语言去实现哈夫曼编码......................................... 4
2.1 C++语言............................................................................................................... 4
2.2 C语言.................................................................................................................. 7
2.3 C#...................................................................................................................... 10
2.4 Java语言............................................................................................................ 16
2.5 JS....................................................................................................................... 21
2.6 Php..................................................................................................................... 25
2.7 面向对象(c++c#phpjsjava) VS 面向过程(c)................................................. 29
2.8 弱类型(jsphp) VS 强类型 (cc++c#java) ..................................................... 29
2.9 visual studio调试js 谷歌调试 VS 直接编译调试.............................................. 29
三、哈夫曼编码的运用场景............................................................................................ 30
3.1 最短路径的应用................................................................................................. 30
3.2 每个消息唯一编码:.......................................................................................... 30
--保证哈夫曼编码唯一性的算法:........................................................................... 32
四、调试过程中遇到的错误集锦:.................................................................................. 32
五、算法的改进方案....................................................................................................... 32
简介
本文用六种语言实现了哈夫曼编码(提供六种语言的源码),主要介绍了算法处理过程当中的存储容器的选择,如何提高算法的效率和6种语言在面向对象面向过程、强类型和弱类型之间在运用过程当中的区别;再者,提供一些编程的调试技巧,及如何保证哈夫曼编码的唯一性;最后介绍哈夫曼编码的特点,并运用这些特点到具体的实例中。
运用以哈夫曼算法为例的来总结一下6中语言的特性,具体到各种语言在具体构造某种关系的时候该如何运用语言特性。
算例
{1、排序;2、构造树结构;3按照左零右一进行编码;4、输出叶子节点编码}
一 、动态构建树及树的存储
1.1 算例转化为伪代码
数据结构:一般会包括两个对象,一是:指示数据所在的位置;而是:表示该位置的值。
树结构:位置{左孩子,右孩子,父节点},值{真实值,代表值} 注:哈夫曼树中的真实值表示节点的权重,代表值表示节点的名称如{A,B,C...}
树结构中,树和节点的关系类似于链表和每个节点之间的关系,接下来介绍具体的节点和树的算法的构造:一棵树有N个节点,所以节点和树的关系是:多对一的关系
伪代码
class Node{ /*节点属性*/ weight; parent; lchild; rchild; value; /*构造函数初始化*/ public Node() { weight=0; parent=-1; lchild=-1; rchild=-1; value=0; } /*节点赋值*/ public Node(weight,parent,lchild,rchild,value) { this.weight=weight; this.parent=parent; this.lchild=lchild; this.value=value; } } class Tree{ Node[]; /*树的构造*/ public Tree(Node[] node) { /*节点排序*/ node//初始节点 sortedNode//排好序后的节点 /*构造树节点*/ for(int i=0;i<node.size();i++) { sortedNode[0];//最小的节点 sortedNode[1];//第二小的节点 /*父节点*/ n = node.size()+i //父节点索引值 node[n+i].weight = sortedNode[0].weight+sortedNode[1]; node[n+i].lchild = sortedNode[0]; node[n+i].rchild = sortedNode[1]; /*临时节点,存储编码*/ sortedNode[0].tempCode = 0; sortedNode[1].tempCode =1; /*最终的叶子节点的编码*/ //遍历当前节点父节点的父节点 p=node[i].parent; while(p!=-1) if(node[p].lchild = i) node[i].Code=+node[p].code; //最终倒叙输出编码 for(j=node[i].Code.size()-1;j>-1;j--) { output(node[i].code); } } }
1.2 树结构中的临时存储结构
/*树的构造中,要注意几个概念:一,临时存储容器、最终的存储容器;二、动态容器,定长容器的选择*/
一、
临时存储容器的结构构造应该以方便存储数组为宜,根据不同的语言特性使用不同的临时容器;
最终的存储容器的构造以最终我们如何取输出我们需要的结构为宜去构造,一般用类对象。
二、
动态容器用于容器中的数据长度(空间),需要动态变化的;一般可作为我们临时容器的选择,存储变化的过程。
定长容器根据我们编程的目标,作为最终的存储容器,用于存放结果。
二、c、c++、c#、php、js、java 不同的语言去实现哈夫曼编码
2.1 C++语言
#include <iostream> using namespace std; #define MAXBIT 100 #define MAXVALUE 10000 #define MAXLEAF 30 #define MAXNODE MAXLEAF*2 -1 typedef struct { int bit[MAXBIT]; //最大的编码字节数组 int start; //编码的开始位置,从高位开始 } HCodeType; /* 编码结构体 */ typedef struct { int weight; int parent; int lchild; int rchild; int value; //节点的实际值,比如说给定节点(ABCDE),value分别为A,B,C,D,E } HNodeType; /* 结点结构体 */ /* 构造一颗哈夫曼树 */ void HuffmanTree (HNodeType HuffNode[MAXNODE], int n) { /* i、j: 循环变量,m1、m2:构造哈夫曼树不同过程中两个最小权值结点的权值, x1、x2:构造哈夫曼树不同过程中两个最小权值结点在数组中的序号。*/ int i, j, m1, m2, x1, x2; /* 初始化存放哈夫曼树数组 HuffNode[] 中的结点*/ for (i=0; i<2*n-1; i++) { HuffNode[i].weight = 0;//权值 HuffNode[i].parent =-1; HuffNode[i].lchild =-1; HuffNode[i].rchild =-1; HuffNode[i].value=i; //实际值,可根据情况替换为字母 } /* end for */ /* 输入 n 个叶子结点的权值 */ for (i=0; i<n; i++) { printf ("Please input weight of leaf node %d: ", i); scanf ("%d", &HuffNode[i].weight); } /* end for */ /* 循环构造 Huffman 树 */ for (i=0; i<n-1; i++) { m1=m2=MAXVALUE; /* m1、m2中存放两个无父结点且结点权值最小的两个结点 */ x1=x2=0; /* 找出所有结点中权值最小、无父结点的两个结点,并合并之为一颗二叉树 */ for (j=0; j<n+i; j++) { if (HuffNode[j].weight < m1 && HuffNode[j].parent==-1) { m2=m1; x2=x1; m1=HuffNode[j].weight; x1=j; } else if (HuffNode[j].weight < m2 && HuffNode[j].parent==-1) { m2=HuffNode[j].weight; x2=j; } } /* end for */ /* 设置找到的两个子结点 x1、x2 的父结点信息 */ HuffNode[x1].parent = n+i; HuffNode[x2].parent = n+i; HuffNode[n+i].weight = HuffNode[x1].weight + HuffNode[x2].weight; HuffNode[n+i].lchild = x1; HuffNode[n+i].rchild = x2; printf("%d ",HuffNode[n+i].weight); printf ("x1.weight and x2.weight in round %d: %d, %d ", i+1, HuffNode[x1].weight, HuffNode[x2].weight); /* 用于测试 */ printf (" "); } /* end for */ } /* end HuffmanTree */ int main(void) { HNodeType HuffNode[MAXNODE]; /* 定义一个结点结构体数组 */ HCodeType HuffCode[MAXLEAF], cd; /* 定义一个编码结构体数组, 同时定义一个临时变量来存放求解编码时的信息 */ int i, j, c, p, n; char pp[100]; printf ("Please input n: "); scanf ("%d", &n); HuffmanTree (HuffNode, n); for (i=0; i < n; i++) { cd.start = n-1; c = i; p = HuffNode[c].parent; while (p != -1) /* 父结点存在 */ { if (HuffNode[p].lchild == c) cd.bit[cd.start] = 0; else cd.bit[cd.start] = 1; cd.start--; /* 求编码的低一位 */ c=p; p=HuffNode[c].parent; /* 设置下一循环条件 */ } /* end while */ /* 保存求出的每个叶结点的哈夫曼编码和编码的起始位 */ for (j=cd.start+1; j<n; j++) { HuffCode[i].bit[j] = cd.bit[j];} HuffCode[i].start = cd.start; } /* end for */ /* 输出已保存好的所有存在编码的哈夫曼编码 */ for (i=0; i<n; i++) { printf ("%d 'Huffman code is: ", i); for (j=HuffCode[i].start+1; j < n; j++) { printf ("%d", HuffCode[i].bit[j]); } printf(" start:%d",HuffCode[i].start); printf (" "); } return 0; }
2.2 C语言
#include <stdio.h> #include<stdlib.h> #define MAXBIT 100 #define MAXVALUE 10000 #define MAXLEAF 30 #define MAXNODE MAXLEAF*2 -1 typedef struct { int bit[MAXBIT]; //最大的编码字节数组 int start; //编码的开始位置,从高位开始 } HCodeType; /* 编码结构体 */ typedef struct { int weight; int parent; int lchild; int rchild; int value; //节点的实际值,比如说给定节点(ABCDE),value分别为A,B,C,D,E } HNodeType; /* 结点结构体 */ /* 构造一颗哈夫曼树 */ void HuffmanTree (HNodeType HuffNode[MAXNODE], int n) { /* i、j: 循环变量,m1、m2:构造哈夫曼树不同过程中两个最小权值结点的权值, x1、x2:构造哈夫曼树不同过程中两个最小权值结点在数组中的序号。*/ int i, j, m1, m2, x1, x2; /* 初始化存放哈夫曼树数组 HuffNode[] 中的结点*/ for (i=0; i<2*n-1; i++) { HuffNode[i].weight = 0;//权值 HuffNode[i].parent =-1; HuffNode[i].lchild =-1; HuffNode[i].rchild =-1; HuffNode[i].value=i; //实际值,可根据情况替换为字母 } /* end for */ /* 输入 n 个叶子结点的权值 */ for (i=0; i<n; i++) { printf ("Please input weight of leaf node %d: ", i); scanf ("%d", &HuffNode[i].weight); } /* end for */ /* 循环构造 Huffman 树 */ for (i=0; i<n-1; i++) { m1=m2=MAXVALUE; /* m1、m2中存放两个无父结点且结点权值最小的两个结点 */ x1=x2=0; /* 找出所有结点中权值最小、无父结点的两个结点,并合并之为一颗二叉树 */ for (j=0; j<n+i; j++) { if (HuffNode[j].weight < m1 && HuffNode[j].parent==-1) { m2=m1; x2=x1; m1=HuffNode[j].weight; x1=j; } else if (HuffNode[j].weight < m2 && HuffNode[j].parent==-1) { m2=HuffNode[j].weight; x2=j; } } /* end for */ /* 设置找到的两个子结点 x1、x2 的父结点信息 */ HuffNode[x1].parent = n+i; HuffNode[x2].parent = n+i; HuffNode[n+i].weight = HuffNode[x1].weight + HuffNode[x2].weight; HuffNode[n+i].lchild = x1; HuffNode[n+i].rchild = x2; printf("%d ",HuffNode[n+i].weight); printf ("x1.weight and x2.weight in round %d: %d, %d ", i+1, HuffNode[x1].weight, HuffNode[x2].weight); /* 用于测试 */ printf (" "); } /* end for */ } /* end HuffmanTree */ int main(void) { HNodeType HuffNode[MAXNODE]; /* 定义一个结点结构体数组 */ HCodeType HuffCode[MAXLEAF], cd; /* 定义一个编码结构体数组, 同时定义一个临时变量来存放求解编码时的信息 */ int i, j, c, p, n; char pp[100]; printf ("Please input n: "); scanf ("%d", &n); HuffmanTree (HuffNode, n); for (i=0; i < n; i++) { cd.start = n-1; c = i; p = HuffNode[c].parent; while (p != -1) /* 父结点存在 */ { if (HuffNode[p].lchild == c) cd.bit[cd.start] = 0; else cd.bit[cd.start] = 1; cd.start--; /* 求编码的低一位 */ c=p; p=HuffNode[c].parent; /* 设置下一循环条件 */ } /* end while */ /* 保存求出的每个叶结点的哈夫曼编码和编码的起始位 */ for (j=cd.start+1; j<n; j++) { HuffCode[i].bit[j] = cd.bit[j];} HuffCode[i].start = cd.start; } /* end for */ /* 输出已保存好的所有存在编码的哈夫曼编码 */ for (i=0; i<n; i++) { printf ("%d 'Huffman code is: ", i); for (j=HuffCode[i].start+1; j < n; j++) { printf ("%d", HuffCode[i].bit[j]); } printf(" start:%d",HuffCode[i].start); printf (" "); } return 0; }
2.3 C#
HuffmanCode.cs
using System; namespace huffman { /// <summary> /// Xia Ping-ping /// 2017-9-7 /// Huffman. /// </summary> public class HuffmanCode{ //huffman 编码类 public int code {get; set; }//编码 public int[] bit { get; set; } public int start { get; set; } } } HuffmanNode.cs using System; namespace huffman { /// <summary> /// Xia Ping-ping /// 2017-9-7 /// Huffman. /// </summary> public class HuffmanNode{ //huffman 节点类 public int weight { get; set; } public int parent { get; set; } public int lchild { get; set; } public int rchild { get; set; } public int tempcode { get; set; }//存放临时编码 public int[] leafcode{ get; set; } //存放叶子节点最后的编码 public HuffmanCode code{ get; set;} }/*end HuffmanNode*/ } Huffman.cs using System; namespace huffman { /// <summary> /// Xia Ping-ping /// 2017-9-7 /// Huffman. /// </summary> public class Huffman { private static Huffman uniqueInstance; private Huffman() { } public static Huffman GetInstance() { if (uniqueInstance == null) { uniqueInstance = new Huffman(); } return uniqueInstance; } //找出序列中最小的两个节点 private static HuffmanNode[] HuffNode; //HuffmanNode[] HuffNode, public static void HuffmanTree(HuffmanNode[] HuffNode, int n) { // HuffNode = new HuffmanNode[2 * n - 1]; //实例化类对象,为Node[] /* i、j: 循环变量,m1、m2:构造哈夫曼树不同过程中两个最小权值结点的权值, x1、x2:构造哈夫曼树不同过程中两个最小权值结点在数组中的序号。*/ int i = 0; int j = 0; int m1 = 0; int m2 = 0; int x1 = 0; int x2 = 0; int[] code = new int[n]; //存放编码 int[] cd;//临时编码 /* 循环构造 Huffman 树 */ for (i = 0; i < n-1; i++) { m1 = m2 = 10000;/* m1、m2中存放两个无父结点且结点权值最小的两个结点 */ x1 = x2 = 0; /* 找出所有结点中权值最小、无父结点的两个结点,并合并之为一颗二叉树 */ for (j = 0; j < n + i; j++) { // HuffNode[j] = new HuffmanNode();//实例化函数的时候需要新建对象 if (HuffNode[j].weight < m1 && HuffNode[j].parent == -1) { m2 = m1; x2 = x1; m1 = HuffNode[j].weight; x1 = j; } else if (HuffNode[j].weight < m2 && HuffNode[j].parent == -1) { m2 = HuffNode[j].weight; x2 = j; } } /* end for */ /* 设置找到的两个子结点 x1、x2 的父结点信息 */ HuffNode[x1].parent = n + i; //父结点序号 HuffNode[x2].parent = n + i; HuffNode[n + i].weight = HuffNode[x1].weight + HuffNode[x2].weight; HuffNode[n + i].lchild = x1; HuffNode[n + i].rchild = x2; HuffNode[n + i].parent = -1; HuffNode[x1].tempcode = 0; HuffNode[x2].tempcode = 1; Console.WriteLine("节点序号:{0}", i + 1); Console.WriteLine("x1.weight,x2.weight:{0}:{1},{2}:{3},", HuffNode[x1].weight, HuffNode[x1].tempcode, HuffNode[x2].weight, HuffNode[x2].tempcode); /* 输出已保存好的所有存在编码的哈夫曼编码 */ }/*end for*/ }/*end function*/ }/*end HuffmanTree class*/ }
Code.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace huffman { class Code { public static void code(HuffmanNode[] huffmanNode,HuffmanCode[] huffmanCode,int n) { int c; int p; for (int i = 0; i < n; i++) { huffmanCode[i].start = n - 1; c = i; p = huffmanNode[c].parent; Console.WriteLine(p); while (p != -1) /* 父结点存在 */ { if (huffmanNode[p].lchild == c) huffmanCode[i].bit[huffmanCode[i].start] = 0; else huffmanCode[i].bit[huffmanCode[i].start] = 1; huffmanCode[i].start--; /* 求编码的低一位 */ c = p; p = huffmanNode[c].parent; /* 设置下一循环条件 */ } /* end while */ } for (int i = 0; i < n; i++) { Console.WriteLine("节点序列号:{0}", i); Console.Write("code:"); for (int j = huffmanCode[i].start + 1; j < n; j++) { Console.Write("{0}", huffmanCode[i].bit[j]); } Console.WriteLine(""); Console.WriteLine("start:{0}", huffmanCode[i].start); }/*end for*/ } } } Progam.cs using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace huffman { class MainClass { public static void Main(string[] args) { Console.WriteLine("Hello World!"); int n = 5; HuffmanNode[] huffmanNode = new HuffmanNode[9]; HuffmanCode[] huffmanCode = new HuffmanCode[5]; List<int> list = new List<int>(); list.Add(7); list.Add(8); list.Add(10); list.Add(3); list.Add(1); list.Add(0); list.Add(0); list.Add(0); list.Add(0); List<int> listP = new List<int>(); listP.Add(-1); listP.Add(-1); listP.Add(-1); listP.Add(-1); listP.Add(-1); listP.Add(-1); listP.Add(-1); listP.Add(-1); listP.Add(-1); List<int> listL = new List<int>(); listL.Add(-1); listL.Add(-1); listL.Add(-1); listL.Add(-1); listL.Add(-1); listL.Add(-1); listL.Add(-1); listL.Add(-1); listL.Add(-1); List<int> listR = new List<int>(); listR.Add(-1); listR.Add(-1); listR.Add(-1); listR.Add(-1); listR.Add(-1); listR.Add(-1); listR.Add(-1); listR.Add(-1); listR.Add(-1); for (int i = 0; i < list.Count; i++) { try { huffmanNode[i] = new HuffmanNode(); huffmanNode[i].weight = list[i]; huffmanNode[i].parent = listP[i]; huffmanNode[i].lchild = listR[i]; huffmanNode[i].rchild = listL[i]; } catch (Exception e) { throw e; } } Huffman.HuffmanTree(huffmanNode, n); //输出编码 List<int> listS = new List<int>(); listS.Add(-1); listS.Add(-1); listS.Add(-1); listS.Add(-1); listS.Add(-1); List<int> listB = new List<int>(); listB.Add(-1); listB.Add(-1); listB.Add(-1); listB.Add(-1); listB.Add(-1); /* 初始化编码数组*/ for (int i = 0; i < n; i++) { huffmanCode[i] = new HuffmanCode(); huffmanCode[i].start = listS[i]; for (int j = 0; j < list.Count; j++) { huffmanCode[i].bit = new int[] { 0, 0, 0, 0, 0 }; } } Code.code(huffmanNode,huffmanCode,n); Console.ReadLine(); } } }
2.4 Java语言
注:红色标注为建的java类文件,.class文件为自动生成的文件
HuffmanCode.java
public class HuffmanCode{ public int start; //huffman 编码类 public int code ; public int[] bit ; }
HuffmanNode.java
public class HuffmanNode{ //huffman 节点类 public int weight ; public int parent ; public int lchild ; public int rchild ; public int value ; public HuffmanCode code; }/*end HuffmanNode*/
Huffman.java
public class Huffman { //懒汉式单例类.在第一次调用的时候实例化自己 private Huffman() {} private static Huffman single=null; //静态工厂方法 public static Huffman getInstance() { if (single == null) { single = new Huffman(); } return single; } public void HuffmanTree(HuffmanNode[] HuffNode,int n) { /* i、j: 循环变量,m1、m2:构造哈夫曼树不同过程中两个最小权值结点的权值, x1、x2:构造哈夫曼树不同过程中两个最小权值结点在数组中的序号。*/ int i=0;int j=0; int m1=0;int m2 = 0;int x1 =0;int x2 = 0; /* 循环构造 Huffman 树 */ for (i=0; i< n-1; i++) { m1=m2=10000;/* m1、m2中存放两个无父结点且结点权值最小的两个结点 */ x1=x2=0; /* 找出所有结点中权值最小、无父结点的两个结点,并合并之为一颗二叉树 */ for (j=0; j< n+i; j++) { if (HuffNode[j].weight< m1 && HuffNode[j].parent==-1) { m2=m1; x2=x1; m1=HuffNode[j].weight; x1=j; } else if (HuffNode[j].weight< m2 && HuffNode[j].parent==-1) { m2=HuffNode[j].weight; x2=j; } } /* end for */ /* 设置找到的两个子结点 x1、x2 的父结点信息 */ HuffNode[x1].parent= n+i; //父结点序号 HuffNode[x2].parent= n+i; HuffNode[n+i] .weight= HuffNode[x1].weight + HuffNode[x2].weight; HuffNode[n+i].lchild = x1; HuffNode[n+i].rchild = x2; HuffNode[n+i].parent= -1; HuffNode[n+i].value= n+i; /*echo HuffNode.parent[x1];echo ";";echo HuffNode.parent[x2]= n+i;*/ System.out.print(" "); System.out.print("节点序号:"); System.out.print(i + 1); System.out.print("x1.weight,x2.weight:"); System.out.print(HuffNode[x1].weight); System.out.print(","); System.out.print(HuffNode[x2].weight); }/*end for*/ }/*end function*/ }/*end HuffmanTree class*/ Code.java public class Code { private Code() {} private static Code single=null; //静态工厂方法 public static Code getInstance() { if (single == null) { single = new Code(); } return single; } public void code(HuffmanNode[] huffmanNode,HuffmanCode[] huffmanCode,int n) { int c; int p; for (int i = 0; i < n; i++) { huffmanCode[i].start = n - 1; c = i; p = huffmanNode[c].parent; while (p != -1) /* 父结点存在 */ { if (huffmanNode[p].lchild == c) huffmanCode[i].bit[huffmanCode[i].start] = 0; else huffmanCode[i].bit[huffmanCode[i].start] = 1; huffmanCode[i].start--; /* 求编码的低一位 */ c = p; p = huffmanNode[c].parent; /* 设置下一循环条件 */ } /* end while */ }/* end for */ for (int i = 0; i < n; i++) { System.out.print(" "); System.out.print("节点序列号:"); System.out.print(i); System.out.print("code:"); for (int j = huffmanCode[i].start + 1; j < n; j++) { System.out.print(huffmanCode[i].bit[j]); }/*end for*/ }/*end for*/ }/*end code*/ } Huffumanjava.java 主类程序 import java.util.LinkedList; import java.util.ArrayList; import java.util.List; public class huffmanjava { public static void main(String[] args) { int n = 5; HuffmanNode[] huffmanNode = new HuffmanNode[9]; HuffmanCode[] huffmanCode = new HuffmanCode[5]; List<Integer> list = new LinkedList<Integer>(); list.add(7); list.add(8); list.add(10); list.add(3); list.add(1); list.add(0); list.add(0); list.add(0); list.add(0); List<Integer> listP = new LinkedList<Integer>(); listP.add(-1); listP.add(-1); listP.add(-1); listP.add(-1); listP.add(-1); listP.add(-1); listP.add(-1); listP.add(-1); listP.add(-1); List<Integer> listL = new LinkedList<Integer>(); listL.add(-1); listL.add(-1); listL.add(-1); listL.add(-1); listL.add(-1); listL.add(-1); listL.add(-1); listL.add(-1); listL.add(-1); List<Integer> listR = new LinkedList<Integer>(); listR.add(-1); listR.add(-1); listR.add(-1); listR.add(-1); listR.add(-1); listR.add(-1); listR.add(-1); listR.add(-1); listR.add(-1); for (int i = 0; i < list.size(); i++) { huffmanNode[i] = new HuffmanNode(); huffmanNode[i].weight = list.get(i); huffmanNode[i].parent = listP.get(i); huffmanNode[i].lchild = listR.get(i); huffmanNode[i].rchild = listL.get(i); } List<Integer> listS = new LinkedList<Integer>(); listS.add(-1); listS.add(-1); listS.add(-1); listS.add(-1); listS.add(-1);//start List<Integer> listB = new LinkedList<Integer>(); listB.add(-1); listB.add(-1); listB.add(-1); listB.add(-1); listB.add(-1);//bit[] //System.out.print(listS.get(0));System.out.print(" "); /* 输出已保存好的所有存在编码的哈夫曼编码 */ for (int i = 0; i < n; i++) { huffmanCode[i] = new HuffmanCode(); huffmanCode[i].start = listS.get(i); System.out.print(huffmanCode[i].start); for (int j = 0; j < listS.size(); j++) { huffmanCode[i].bit = new int[]{0,0,0,0,0}; huffmanCode[i].bit[j] = listS.get(j); } } Huffman.getInstance().HuffmanTree(huffmanNode,n); Code.getInstance().code(huffmanNode, huffmanCode,n); } }
2.5 JS
<script> var huffmanNode = new Object();//定义对象类型,模拟结构体、类类型 huffmanNode.weight = 0; huffmanNode.parent = -1; huffmanNode.lchild = -1; huffmanNode.rchild = -1; huffmanNode.value = -1; huffmanNode.tempcode = 0; var huffmanCode = new Object();//定义对象类型,模拟结构体、类类型 huffmanCode.start = 0; huffmanCode.bit = new Array();//定义数组 function MinTwo(huffmanTree,n) { var m1=10000,m2=10000,x1=0,x2=0; for(var i=0;i<n;i++) { if(huffmanTree.weight[i]<m1 && huffmanTree.parent[i]==-1 ) { m2 =m1; m1 = huffmanTree.weight[i]; x1=i; }else if(huffmanTree.weight[i]<m2 && huffmanTree.parent[i]==-1 ) { m2 = huffmanTree.weight[i]; x2 = i; //console.log(x2); } } var minArr= new Array(x1,x2);//存放//存放 x1,x2 return minArr; } //循环构造huffman树 function HuffmanTree(huffmanNode,n) { var x1=x2=0; //设置找到的两个子节点的信息 for(var i=0;i<n-1;i++) { //var minArr = MinTwo(huffmanNode,n); //x1 = minArr[0]; x2 = minArr[1]; var m1=10000,m2=10000,x1=0,x2=0; for(var j=0;j<n+i;j++) { if(huffmanNode.weight[j]<m1 && huffmanNode.parent[j]==-1 ) { m2 =m1; x2=x1; m1 = huffmanNode.weight[j]; x1=j; }else if(huffmanNode.weight[j]<m2 && huffmanNode.parent[j]==-1 ) { m2 = huffmanNode.weight[j]; x2 = j; //console.log(x2); } } huffmanNode.parent[x1] = n+i; huffmanNode.parent[x2] = n+i; huffmanNode.weight[n+i] = huffmanNode.weight[x1]+huffmanNode.weight[x2]; huffmanNode.lchild[n+i] = x1; huffmanNode.rchild[n+i] = x2; huffmanNode.parent[n+i] = -1; huffmanNode.value[n+i] = n+i; huffmanNode.code[x1] = 0 ; huffmanNode.code[x2] = 1; console.log (" "); console.log(huffmanNode.weight[n+i]); console.log ("x1.weight and x2.weight in round : "); console.log( i+1, huffmanNode.weight[x1], huffmanNode.weight[x2]); /* 用于测试 */ } return huffmanNode; } function Code(huffmanNode,huffmanCode,n)/* 定义一个编码结构体数组 */ /* 定义一个结点结构体数组 */ { var bit=new Array();//存放编码 var start=0;//start位置 for (i=0; i<n; i++) { start = n-1; c = i; p = huffmanNode.parent[c]; while (p != -1) /* 父结点存在 */ { if (huffmanNode.lchild[p] == c) bit[start] = 0; else bit[start] = 1; start--; /* 求编码的低一位 */ c=p; p=huffmanNode.parent[c]; /* 设置下一循环条件 */ } /* end while */ /* 保存求出的每个叶结点的哈夫曼编码和编码的起始位 */ for (var j=start+1; j<n; j++) { huffmanCode.bit[i][j] = bit[j];} huffmanCode.start[i] = start; } /* end for */ var str =""; document.getElementById("showAll").innerHTML+="<p>huffmanCode</p>"; //2、使用appendChild: /* 输出已保存好的所有存在编码的哈夫曼编码 */ for (i=0; i<n; i++) { var newchild = document.createElement("p"); console.log ("节点序列号:"); console.log(i); console.log (","); newchild.innerHTML ="节点序列号:"+i+";code:"; document.getElementById("showAll").appendChild(newchild); for (j=huffmanCode.start[i]+1; j <n; j++) { var newchild1 = document.createElement("p"); newchild1.innerHTML = huffmanCode.bit[i][j]; console.log(huffmanCode.bit[i][j]); document.getElementById("showAll").appendChild(newchild1); } console.log(" start:"); console.log(huffmanCode.start[i]); console.log(" "); //document.getElementById("test").value= str; }/*end for*/ }/*end function*/ function Click(){ alert(1); var n = 5; var huffmanNode = new Object();//定义对象类型,模拟结构体、类类型 huffmanNode.weight = new Array(7,8,3,10,1); huffmanNode.parent = new Array(-1,-1,-1,-1,-1); huffmanNode.lchild = new Array(-1,-1,-1,-1,-1); huffmanNode.rchild = new Array(-1,-1,-1,-1,-1); huffmanNode.value = new Array(-1,-1,-1,-1,-1); huffmanNode.code = new Array(-1,-1,-1,-1,-1); for(var i=0;i<n;i++) { huffmanCode.bit[i] = new Array(-1,-1,-1,-1,-1); } huffmanCode.start = new Array(-1,-1,-1,-1,-1); //MinTwo(huffmanNode,5); huffmanNode = HuffmanTree(huffmanNode,n); Code(huffmanNode,huffmanCode,n); } </script> <html> <title>huffman树</title> <body> <input type="button" id = "test" value="ClickMe" onclick="Click()"> <div id="showAll"><div> </body> </html>
2.6 Php
<?php ini_set("memory_limit","-1"); error_reporting( E_ALL&~E_NOTICE ); class HuffmanCode{ var $bit; //var $bit=array(); //最大的编码字节数组 var $start; //编码的开始位置,从高位开始 } class HuffmanNode{ var $weight=array(); var $parent; var $lchild; var $rchild; var $value; //节点的实际值,比如说给定节点(ABCDE),value分别为A,B,C,D,E } class HuffmanTree{ //静态变量保存全局实例 private static $_instance = null; //私有构造函数,防止外界实例化对象 private function __construct() { } //私有克隆函数,防止外办克隆对象 private function __clone() { } //静态方法,单例统一访问入口 static public function getInstance() { if (is_null ( self::$_instance ) || isset ( self::$_instance )) { self::$_instance = new self (); } return self::$_instance; } function HuffmanTree(&$HuffNode,$n) { /* i、j: 循环变量,m1、m2:构造哈夫曼树不同过程中两个最小权值结点的权值, x1、x2:构造哈夫曼树不同过程中两个最小权值结点在数组中的序号。*/ $i;$j; $m1;$m2;$x1;$x2; /* 循环构造 Huffman 树 */ for ($i=0; $i<$n-1; $i++) { $m1=$m2=10000;/* m1、m2中存放两个无父结点且结点权值最小的两个结点 */ $x1=$x2=0; /* 找出所有结点中权值最小、无父结点的两个结点,并合并之为一颗二叉树 */ for ($j=0; $j<$n+$i; $j++) { if ($HuffNode->weight[$j]< $m1 && $HuffNode->parent[$j]==-1) { $m2=$m1; $x2=$x1; $m1=$HuffNode->weight[$j]; $x1=$j; } else if ($HuffNode->weight[$j]< $m2 && $HuffNode->parent[$j]==-1) { $m2=$HuffNode->weight[$j]; $x2=$j; } } /* end for */ /* 设置找到的两个子结点 x1、x2 的父结点信息 */ $HuffNode->parent[$x1]= $n+$i; //父结点序号 $HuffNode->parent[$x2]= $n+$i; $HuffNode->weight[$n+$i] = $HuffNode->weight [$x1]+ $HuffNode->weight[$x2]; $HuffNode->lchild[$n+$i] = $x1; $HuffNode->rchild[$n+$i] = $x2; $HuffNode->parent[$n+$i]= -1; $HuffNode->value[$n+$i]= $n+$i; /*echo $HuffNode->parent[$x1];echo ";";echo $HuffNode->parent[$x2]= $n+$i;*/ echo " "; echo "节点序号:"; echo $i+1 ; echo ";"; echo "x1.weight,x2.weight:"; echo $HuffNode->weight[$x1]; echo ","; echo $HuffNode->weight[$x2]; }/*end for*/ }/*end function*/ }/*end HuffmanTree class*/ class Code{ //静态变量保存全局实例 private static $_instance = null; //私有构造函数,防止外界实例化对象 private function __construct() { } //私有克隆函数,防止外办克隆对象 private function __clone() { } //静态方法,单例统一访问入口 static public function getInstance() { if (is_null ( self::$_instance ) || isset ( self::$_instance )) { self::$_instance = new self (); } return self::$_instance; } var $i,$j, $c, $p, $n; var $pp=array(); function Code(&$HuffmanCode,&$HuffmanNode,$n)/* 定义一个编码结构体数组 */ /* 定义一个结点结构体数组 */ { $bit=array();//存放编码 $start;//$start位置 for ($i=0; $i<$n; $i++) { $start = $n-1; $c = $i; $p = $HuffmanNode->parent[$c]; //接 while ($p != -1) /* 父结点存在 */ { if ($HuffmanNode->lchild[$p] == $c) $bit[$start] = 0; else $bit[$start] = 1; $start--; /* 求编码的低一位 */ $c=$p; $p=$HuffNode->parent[$c]; /* 设置下一循环条件 */ } /* end while */ /* 保存求出的每个叶结点的哈夫曼编码和编码的起始位 */ for ($j=$start+1; $j<$n; $j++) { $HuffmanCode->bit[$i][$j] = $bit[$j];} $HuffmanCode->start[$i] = $start; } /* end for */ /* 输出已保存好的所有存在编码的哈夫曼编码 */ for ($i=0; $i<$n; $i++) { echo $i;echo ":"; for ($j=$HuffmanCode->start[$i]+1; $j <$n; $j++) { echo $HuffmanCode->bit[$i][$j]; } echo" start:";echo $HuffmanCode->start[$i]; echo" "; echo " "; }/*end for*/ }/*end function*/ }/*end code class*/ ?> <?php ini_set('memory_limit',"256M") ; //调用function $db = DB::getInstance(); $HuffmanNode = new HuffmanNode(); $HuffmanNode->weight = array(7,3,8,10,1); $HuffmanNode->parent = array(-1,-1,-1,-1,-1); $HuffmanNode->lchild = array(-1,-1,-1,-1,-1); $HuffmanNode->rchild = array(-1,-1,-1,-1,-1); $HuffmanNode->value = array(1,2,3,4,5); $HuffmanTree =HuffmanTree::getInstance(); $HuffmanTree->HuffmanTree($HuffmanNode,5); $HuffmanCode = new HuffmanCode(); //$cd = HuffmanCode::getInstance(); $Code = Code::getInstance(); $Code->Code($HuffmanCode,$HuffmanNode,5); /*$HuffmanTree = new HuffmanTree($HuffmanNode,5); $HuffmanTree->HuffmanTree($HuffmanNode,5); $HuffmanCode = new HuffmanCode(); $cd = new HuffmanCode(); $Code = new Code($HuffmanCode,$HuffmanNode,5,$cd); $Code->Code($HuffmanCode,$HuffmanNode,5,$cd);*/ ?>
2.7 面向对象(c++c#phpjsjava) VS 面向过程(c)
面向对象(c++c#phpjsjava) VS 面向过程(c):在同一个阶段用这六种语言去实现同一种算法,除了语言障碍之外,还有一个重要的是在面向对象和面向过程之间的思想切换。
笔者最初是以C语言开始的,按照算例一步步进行下来,分别用两个结构体来作为节点和编码的存储结构。之后的按照“过程”进行一系列的编程。
之后,用php去实现。首先对着C的实现去用php翻译一次:php的类结构 取代 C的结构体; 结构体不能定义成员函数,所以不能初始实例化对象; 而类能用属性取代结构体中变量的同时,还能够通过构造函数来初始化实例对象,避免了在类的调用中出现的异常。
当然其他的面向对象的语言同样具有该特性。
2.8 弱类型(jsphp) VS 强类型 (cc++c#java)
虽然不管是弱类型还是强类型的面向对象的语言,都是用对象去调用方法。在声明接收方法的变量时,强类型语言的java是必须使用对应的类去接收对应的对象的,否则会出现异常。
所以使用强类型语言去定义临时存储变量时,要特别注意变量的类型是否与该方法的所申明的变量一致。
2.9 visual studio调试js 谷歌调试 VS 直接编译调试
visual studio调试js 谷歌调试能够下断点,悬停取值进行调试,比较方便;但是直接编译调试,不是那么方便了,我们需要输出调试,但是输出调试也具有技巧。可以使用输出调试的方法模拟断点调试,具体的应用场景有:while循环体中,先定义一个全局的常量,在循环体中递减来判断循环的变量是否正常;此方法对于某些无限循环的循环体十分有效。
更多的模拟断点调试的技巧还会在后续不断总结。
三、哈夫曼编码的运用场景
首先哈夫曼编码算法的特点:求最短路径、求每个消息的唯一标识码、利用哈夫曼编码求解消息的最短编码。
3.1 最短路径的应用
当有一大批数据需输入比较时,我们需要优化一下比较算法,利用二叉树进行封装,使得比较的次数最少。
3.2 每个消息唯一编码
每个消息码的编码都是唯一的,利用消息编码的唯一性,作为消息的前缀码进行文件的加密传输。但由于编码不唯一性,那么解码的时候的消息就不能保证与原文的消息一致。
图2-1组成最优哈夫曼树的所有情况
3.3 造成哈夫曼编码不唯一性的原因
教科书式的哈夫曼编码过程的描述:
(1)给定一个序列{A,B,C,D,E},序列中的每个节点具有权重(分布频率);
(2)找出节点中,权重最小的两个节点,使最小的节点值权重相加成为新节点的权重。
(3)新的节点序列:去掉(2)中的最小的两个节点,加上新生成的节点构成新的节点序列;
(4)重复(2)(3)直到新生成的节点序列中只剩一个节点。
上述描述中不唯一性的体现:当两个节点的分布频率是一样的情况下,按照(2)中的描述既可以置于父节点的左边,也可置于右边;按照(3)中的描述,如果新生成的节点频率的分布与节点序列中的分布相同时可以置于父节点的左边,也可置于右边;当节点序列中有多个(>2)相同的频率分布时,既可以生成一个新的树分支,也可以在原分支分支上置于左孩子节点或有孩子节点。综上,当节点序列中出现多个(>=2)分布频率一致的情况下,就会出现编码不唯一的情况
3.4 保证哈夫曼编码唯一性的算法:
(1)给定一个序列{A,B,C,D,E},序列中的每个节点具有权重(分布频率);
(2)找出节点中,权重最小的两个节点,使最小的节点值权重相加成为新节点的权重。如果节点的分布频率相同,在节点序列中出现的位置在前定义为最小的节点,出现的位置在后定义为次小的节点。
(3)新的节点序列:去掉(2)中的最小的两个节点,加上新生成的节点构成新的节点序列;新生成的节点位置在原节点序列之后。
(4)重复(2)(3)直到新生成的节点序列中只剩一个节点。
这个算法的描述中添加两个条件(分布频率、序列中的位置)判定最小和次小节点,保证了哈夫曼编码的唯一性。
四、调试过程中遇到的错误集锦:
(1)php: Cannot allocate memory
由于PHP配置文件中是会有最大的内存空间的限制的,当运行内存超过最大值时,会出现内存无法分配的情况。但是对于一般情况下的运行是绝对可以的,出现内存分配不足的情况极有可能是出现了死循环。
(2)C#:输出树时循环了,输出了两组树;如果算法不存在错误时,极有可能是实例化类对象的时候实例化了两次。当对象实例化后不需要重新创建对象,这时使用单例模式,防止外部类的多次访问造成多次类的实例化。
(3)java:主函数的类名一定要和项目名(或主函数文件名一致),否则编译后会报错:无法加载主类。
java使用List 泛型容器:
import java.util.LinkedList;/*加载类库*/
import java.util.ArrayList;
import java.util.List;
List<Integer> list = new LinkedList<Integer>();/*创建list泛型容器*/
五、算法的改进方案
(1)保证唯一性:算法见3中的改进描述
(2)存储树结构:使用链表动态存储,避免使用多个数组浪费空间
(3)对随机序列,计算分布频率,并进行加解码。验证哈夫曼编码的唯一性。