哈夫曼编码问题

本博客的代码的思想和图片参考:好大学慕课浙江大学陈越老师、何钦铭老师的《数据结构》

哈夫曼编码问题

1 引子

1.1

将百分制的考试成绩转换成五分制的成绩,程序如下:


if( score < 60 ) grade =1;
else if( score < 70 ) grade =2;
else if( score < 80 ) grade =3;
else if( score < 90 ) grade =4;
else grade =5;

 

那么上面这个其实是一棵判断树:

 

我们发现,在这个判断树中,60分以下的需要查找一次,60-69的需要查找两次

70-79需要查找三次,80-89的需要查找四次,90以上的需要查找五次

如果全部的分数集中在7080之间,那么这棵树就不够优化。

 

1.2

如果我们考虑学生的成绩分布情况,如下表所示:

分数段

0-59

60-69

70-79

80-89

90以上

比例

0.05

0.15

0.40

0.30

0.10

 

根据上面的判断树和学生成绩的分布情况,那么我们计算平均的查找效率:

0.05*1+0.15*2+0.40*3+0.30*4+0.10*4=3.15

如何我们重新设计程序,让频率高优先判定,那么就会减少判定的次数,我们重新设计判断程序和判断树

 

if(score<80){

       if(score<70){

            if(score<60){

                grade=1;

            }else{

                grade=2;

            }

       }else{

            grade=3;

       }

     }else{

         if(score<90){

            grade=4;

         }else{

            grade=5;

         }

     }

 

这样的判断树就如下所示:

 

 

分数段

0-59

60-69

70-79

80-89

90以上

比例

0.05

0.15

0.40

0.30

0.10

 

那么我们再根据判断树和分数的频率来计算平均查找效率:

0.05*3+0.15*3+0.4*2+0.3*2+0.10*2=2.2

平均查找效率明星变高了。

 

思考:如果根据节点的频率不同,构造更高效率的搜索树

 

2 哈夫曼树

2.1 定义

定义:带权路径长度(WPL):设二叉树有n个叶子节点,每个节点带有权值,从根节点到每个叶子节点的长度为,则每个叶子的带权路径之和为

最优二叉树或者哈夫曼数:WPL值最小

 

2.2 例子

现有五个叶子节点,他们的权值为{1,2,3,4,5},用此权值序列可以构造多个不同形状的二叉树。

 

 

 

3哈夫曼编码

3.1例子

 给定一段字符串,如何对字符进行编码,可以使得该字符串的
存储空间最少?
[] 假设有一段文本,包含58个字符,并由以下7个字符构: a
st,空格( sp),换行( nl);这7个字符出现的次数不同。如
7个字符进行编码,使得总编码空间最少?
分析】
1)用等长ASCII编码: 58 ×8 = 464位;
2)用等长3位编码: 58 ×3 = 174位;
3)不等长编码:出现频率高的字符用的编码短些,出现频率低
的字符则可以编码长些?

 

3.2如何进行不等长编码

3.2.1 编码的二义性

例如:

a:1

e:0

s:10

t:11

那么1011是什么编码

aeza:1101

aet:1101

st:1011

 

这样就产生了编码的二义性。

 

 

那么如何避免二义性呢?使用前缀码

前缀码(prefix code):任何字符的编码都不是另一个字符编码的前缀,就可以无二义的解码

 

3.2.2 使用二叉树进行编码

使用二叉树进行编码:

a):左右分支表示0,1

b)字符只在叶子节点上

 

四个字符的频率:

a:4,u:1,x:2,z:1

那么我们可以使用二叉树进行编码

 

 

可以看出,使用二叉树进行哈夫曼编码可以减少编码的总长度

 

 

3.3 哈夫曼编码的一个具体的例子

该例子出自于:慕课浙江大学数据结构陈越老师何钦铭老师

 

需求:

 

 

 

 

 

二叉树的哈夫曼编码

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/yghjava/p/6751582.html

### Java 实现哈夫曼编码 #### 哈夫曼树构建过程 在Java中实现哈夫曼编码,首先要创建一个节点类`Node`用于表示哈夫曼树的各个结点。该类应包含权重、字符以及指向左子节点和右子节点的指针。 ```java class Node implements Comparable<Node> { char ch; int weight; Node left, right; public Node(char ch, int weight) { this.ch = ch; this.weight = weight; } @Override public int compareTo(Node other) { return Integer.compare(this.weight, other.weight); } } ``` 接下来定义方法`createHT`来构建哈夫曼树。此函数接收一组字符及其对应的频率作为输入参数,并返回一棵完整的二叉树结构[^1]。 ```java public static Node createHT(Map<Character, Integer> freqMap) { PriorityQueue<Node> pq = new PriorityQueue<>(); // 将所有字符加入优先队列 for (var entry : freqMap.entrySet()) { pq.add(new Node(entry.getKey(), entry.getValue())); } while(pq.size() != 1){ Node n1 = pq.poll(); Node n2 = pq.poll(); Node parent = new Node('\0', n1.weight + n2.weight); parent.left = n1; parent.right = n2; pq.offer(parent); } return pq.peek(); } ``` #### 编码生成逻辑 一旦获得了哈夫曼树,则可以通过递归方式遍历整棵树并记录路径上的'0'(向左走)'1'(向右走),从而得到每个叶子节点所代表字符的具体编码串。这里可以借助辅助函数完成: ```java private void buildCodes(Node node, String codeStr, Map<Character, String> codes) { if(node == null) return ; if(Character.isDefined(node.ch)) codes.put(node.ch, codeStr); buildCodes(node.left , codeStr+"0",codes); buildCodes(node.right, codeStr+"1",codes); } // 调用buildCodes初始化编码映射表 public Map<Character, String> generateHuffmanCode(Node root) { Map<Character, String> huffManCodes = new HashMap<>(); buildCodes(root,"",huffManCodes ); return huffManCodes ; } ``` #### 字符串压缩解压功能 有了上述准备之后就可以轻松地编写两个主要的功能——字符串到其哈夫曼编码形式之间的转换(`encode`);反之亦然(`decode`)。 ```java public String encode(String inputText, Map<Character, String> codes) { StringBuilder encodedString = new StringBuilder(); for (char c : inputText.toCharArray()) encodedString.append(codes.get(c)); return encodedString.toString(); } public String decode(String encodedData, Node rootNode) { StringBuilder decodedOutput = new StringBuilder(); Node current = rootNode; for (char bit : encodedData.toCharArray()){ if(bit=='0') current=current.left; else if(bit=='1') current=current.right; if(current!=null && Character.isLetterOrDigit(current.ch)){ decodedOutput.append(current.ch); current=rootNode; } } return decodedOutput.toString(); } ``` 通过以上步骤,在Java环境中实现了基本的哈夫曼编码机制,能够有效地减少原始数据量的同时保持信息不失真[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值