哈夫曼的编码实验
一、哈夫曼编码介绍:
- 1、哈夫曼树:
(1)定义:假设有n个权值{w1, w2, ..., wn},试构造一棵含有n个叶子结点的二叉树,每个叶子节点带权威wi,则其中带权路径长度WPL最小的二叉树叫做最优二叉树或者哈夫曼树。
(2)特点:哈夫曼树中没有度为1的结点,故由n0 = n2+1以及m= n0+n1+n2,n1=0可推出m=2*n0-1,即一棵有n个叶子节点的哈夫曼树共有2n-1个节点。 - 2、哈夫曼编码步骤:
- (1)计算字符出现的次数:
- 假设我们现在有一段文档需要进行编码,我们现在需要对于每一个字符出现的次数进行统计,然后分别计算出其在这篇文档的权重,也就是频率,我们用下面的一段文字进行举例;
- 在如下的文档中包括的字符有8个字母和2个空格,所以我们现在需要去统计这10个字符的个数,统计后的数据如下:
- (2)对字符出现的次数作为一个权重,从小到大进行排序;
- (3)依照排序结果从小到大根据以下规则进行造树:
- a、最小的前两个数进行权重的相加,形成他们两个作为左子树和右子树的父结点,如果是左结点就标记为0,如果是右结点就标记为1
- b、然后将父结点作为一个新的结点进入排序结果,之后进行重新排序(ps:假如出现添加后的父结点的权重和之前排序中的结点的权重相等的情况,两者的位置具体是对于建树没有影响的,但是在编程实现的过程中,程序需要将两者的位置进行确定)
- c、重复上述过程,直到得到的父结点的权重为1。
- (1)计算字符出现的次数:
代码构建过程:
字符出现的次数统计:
String source="We go the hustler you've got to hustle There ain't no time for sleep Just keep on movin' do what you're doing ";
String[] b = source.split("");
int an=0;
int bn=0;
int cn=0;
int dn=0;
int en=0;
int fn=0;
int gn=0;
int hn=0;
int in=0;
int jn=0;
int kn=0;
int ln=0;
int mn=0;
int nn=0;
int on=0;
int pn=0;
int qn=0;
int rn=0;
int sn=0;
int tn=0;
int un=0;
int vn=0;
int wn=0;
int xn=0;
int yn=0;
int zn=0;
int zkongge=0;
for (int i =0;i<b.length;i++){
if (b[i].equals("a")){
an++;
}
if (b[i].equals("b")){
bn++;
}
if (b[i].equals("c")){
cn++;
}
if (b[i].equals("d")){
dn++;
}
if (b[i].equals("e")){
en++;
}
if (b[i].equals("f") ){
fn++;
}
if (b[i].equals("g")){
gn++;
}
if (b[i].equals("h")){
hn++;
}
if (b[i].equals("i")){
in++;
}
if (b[i].equals("j")){
jn++;
}
if (b[i].equals("k")){
kn++;
}
if (b[i].equals("l")){
ln++;
}
if (b[i].equals("m")){
mn++;
}
if (b[i].equals("n")){
nn++;
}
if (b[i].equals("o")){
on++;
}
if (b[i].equals("p")){
pn++;
}
if (b[i].equals("q")){
qn++;
}
if (b[i].equals("r")){
rn++;
}
if (b[i].equals("s")){
sn++;
}
if (b[i].equals("t")){
tn++;
}
if (b[i].equals("u")){
un++;
}
if (b[i].equals("v")){
vn++;
}
if (b[i].equals("w")){
wn++;
}
if (b[i].equals("x")){
xn++;
}
if (b[i].equals("y")){
yn++;
}
if (b[i].equals("z")){
zn++;
}
}
Objects Za= new Objects("a",an);
Objects Zb = new Objects("b",bn);
Objects Zc = new Objects("c",cn);
Objects Zd = new Objects("d",dn);
Objects Ze = new Objects("e",en);
Objects Zf = new Objects("f",fn);
Objects Zg = new Objects("g",gn);
Objects Zh = new Objects("h",hn);
Objects Zi = new Objects("i",in);
Objects Zj = new Objects("j",jn);
Objects Zk = new Objects("k",kn);
Objects Zl = new Objects("l",ln);
Objects Zm = new Objects("m",mn);
Objects Zn = new Objects("n",nn);
Objects Zo = new Objects("o",on);
Objects Zp = new Objects("p",pn);
Objects Zq = new Objects("q",qn);
Objects Zr = new Objects("r",rn);
Objects Zs = new Objects("s",sn);
Objects Zt = new Objects("t",tn);
Objects Zu = new Objects("u",un);
Objects Zv = new Objects("v",vn);
Objects Zw = new Objects("w",wn);
Objects Zx = new Objects("x",xn);
Objects Zy = new Objects("y",yn);
Objects Zz = new Objects("z",zn);
System.out.println("各个字符的概率统计为:");
Objects[] temp = new Objects[]{Za,Zb,Zc,Zd,Ze,Zf,Zg,Zh,Zi,Zj,Zk,Zl,Zm,Zn,Zo,Zp,Zq,Zr,Zs,Zt,Zu,Zv,Zw,Zx,Zy,Zz};
2、哈夫曼编码&解码:
private Map<Character,Integer> map=new HashMap<>();
private Map<Character,String> ce=new HashMap<>();
private PriorityQueue<Tree> trees=new PriorityQueue<>();
private String source;
private String result;
public void init(String source){
this.source=source;
char[] chars= source.toCharArray();
for (char c :chars){
if (!map.containsKey(c)){
map.put(c,1);
}else {
map.put(c,map.get(c)+1);
}
}
afterInit();
}
private void afterInit() {
map.forEach((c,count)->{
Node<Character> node=new Node<>();
node.key=count;
node.charData=c;
Tree tree=new Tree();
tree.setRoot(node);
trees.add(tree);
});
}
public void build(){
while (this.trees.size()>1){
Tree left=this.trees.poll();
Tree right=this.trees.poll();
Node node=new Node();
node.key=left.getRoot().key+right.getRoot().key;
node.leftChild=left.getRoot();
node.rightChild=right.getRoot();
left.setRoot(node);
this.trees.add(left);
}
}
public void encoding(){
Tree tree=this.trees.peek();
this.encoding(tree.getRoot(),"");
}
public String encodingResult(){
StringBuilder sb=new StringBuilder();
char[] chars= source.toCharArray();
for (char c :chars){
sb.append(ce.get(c)+' ');
}
return sb.toString();
}
private void encoding(Node<Character> node,String encoding){
if (node!=null){
if (node.leftChild==null && node.rightChild==null){
ce.put(node.charData,encoding);
}
encoding(node.leftChild,encoding+"0");
encoding(node.rightChild,encoding+"1");
}
}
public void displayTree(){
Tree tree=this.trees.peek();
tree.inDisplay(tree.getRoot());
}
public void displayEncodeing(){
ce.forEach((k,v)->{
System.out.println(k+":"+v);
});
}
3、中序遍历建立哈夫曼树:
package Exp9.Hufman;
public class Tree implements Comparable<Tree>{
private Node root;
public Node getRoot() {
return root;
}
public void setRoot(Node root) {
this.root = root;
}
/**
*中序遍历法
*/
public void inDisplay(Node node){
if (node!=null){
inDisplay(node.leftChild);
System.out.println(node.key+":"+node.charData);
inDisplay(node.rightChild);
}
}
public void postDisplay(Node node){
if (node!=null){
postDisplay(node.leftChild);
postDisplay(node.rightChild);
System.out.print(node.key+":"+node.charData);
}
}
@Override
public int compareTo(Tree o) {
if (this.root.key>o.root.key){
return 1;
}else if (this.root.key==o.root.key){
return 0;
}else {
return -1;
}
}
}