哈夫曼编码

哈夫曼树又称最优树(二叉树),是一类带权路径最短的树。构造这种树的算法最早是由哈夫曼(Huffman)1952年提出,这种树在信息检索中很有用。

结点之间的路径长度:从一个结点到另一个结点之间的分支数目。

树的路径长度:从树的根到树中每一个结点的路径长度之和。

结点的带权路径长度:从该结点到树根之间的路径长度与结点上权的乘积。

树的带权路径长度:树中所有叶子结点的带权路径长度之和,记作:

                                

    WPL为最小的二叉树就称作最优二叉树或哈夫曼树。

    完全二叉树不一定是最优二叉树。

    哈夫曼树的构造

(1)根据给定的n个权值{w1,w2,...,wn}构造n棵二叉树的集合F={T1,T2,...,Tn},其中Ti中只有一个权值为wi的根结点,左右子树为空;
(2)在F中选取两棵根结点的权值为最小的数作为左、右子树以构造一棵新的二叉树,且置新的二叉树的根结点的权值为左、右子树上根结点的权值之和。
(3)将新的二叉树加入到F中,删除原两棵根结点权值最小的树;
(4)重复(2)和(3)直到F中只含一棵树为止,这棵树就是哈夫曼树。

例1:

例2:

结点的存储结构:

构造哈夫曼树的算法说明:

#define n                  /* 叶子总数 */
#define  m  2*n-1      /* 结点总数 */ 
证:由性质3,叶子结点数 n0=n2+1,故哈夫曼树结点总数为 n0+n2=n0+(n0-1)=2*n0-1

例3 在解某些判定问题时,利用哈夫曼树获得最佳判定算法。

(a)

WPL=0.05*1+0.15*2+0.4*3+0.3*4+0.1*4=3.15

(b)(c)

WPL=0.4*1+0.3*2+0.15*3+0.05*4+0.1*4=2.05                    WPL=0.05*3+0.15*3+0.4*2+0.3*2+0.1*2=2.2

哈夫曼编码

    从哈夫曼树根结点开始,对左子树分配代码“0”,右子树分配代码“1”,一直到达叶子结点为止,然后将从树根沿每条路径到达叶子结点的代码排列起来,便得到了哈夫曼编码。

例,对电文 EMCAD 编码。若等长编码,则

    EMCAD => 000001010011100 共15位

设各字母的使用频度为 {E,M,C,A,D}={1,2,3,3,4}。用频度为权值生成哈夫曼树,并在叶子上标注对应的字母,树枝分配代码“0”或“1”:

  

各字母的编码即为哈夫曼编码:  EMCAD => 000001011011 共12位




package arg;



import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;


public class HuffmanCode1 extends Tree {


public HuffmanCode1() {
init();
}


/**
* 初始化节点值并构造最优二叉树

*/


public void init() {
super.getLeafWeight();
super.makeBestTree();
}


/**
* 生成赫夫曼编码的递归函数

* @param t
*            TNode 当前遍历节点
* @param s
*            String 目前遍历至此的赫夫曼编码
*/
protected void hufT(TNode t, String s) {
if (t.isLeaf()) {
t.setHuffCode(s);
} else {
hufT(t.lChild, s + "0");
hufT(t.rChild, s + "1");
}
}


/**
* 生成赫夫曼编码的外部调用函数

*/
public void makeHuffCode() {
hufT(root, "");


}


/**
* 查看所有的赫夫曼编码值

*/
public void viewHuffCode() {
for (int i = 0; i < leafArr.length; i++) {
System.out.println(leafArr[i].w + ":" + leafArr[i].getHuffCode());
}
}


public static void main(String[] args) {
HuffmanCode1 hc = new HuffmanCode1();
hc.makeHuffCode();
hc.viewHuffCode();
}
}


class TNode {
/** 权值 */
protected int w;
/** 左孩子节点 */
TNode lChild = null;
/** 右孩子节点 */
TNode rChild = null;


/** (仅对叶子节点有效)赫夫曼编码 */
String huffCode = null;


/**
* 构造一个赫夫曼编码节点的实例。设定左右子树和权值

* @param w
*            int 权值
* @param l
*            TNode 左孩子节点
* @param r
*            TNode 右孩子节点
*/
public TNode(int w, TNode l, TNode r) {
this.w = w;
lChild = l;
rChild = r;
}


/**
* 构造一个赫夫曼编码叶子点的实例。仅仅设定权值

* @param w
*            int 权值
*/
public TNode(int w) {
this.w = w;
}


/**
* 判断本节点是否为叶子节点

* @return boolean 为叶子节点则返回true
*/
public boolean isLeaf() {
return (rChild == null && lChild == null);
}


/**
* 返回本节点的权值

* @return int 本节点的权值
*/
public int getWeight() {
return w;
}


/**
* 返回本节点的左孩子节点

* @return TNode 左孩子节点
*/
public TNode leftChild() {
return lChild;
}


/**
* 返回本节点的右孩子节点

* @return TNode 右孩子节点
*/
public TNode rightChild() {
return rChild;
}


/**
* 设置节点的赫夫曼编码

* @param str
*            String 被设置的赫夫曼编码
*/
public void setHuffCode(String str) {
huffCode = str;
}


/**
* 得到节点的赫夫曼编码

* @return String 被设置的赫夫曼编码
*/
public String getHuffCode() {
return huffCode;
}
}


/**
 * 
 最优二叉树类
 * 
 * 
 * @version 1.0, 2007-01-8
 * @author 李赫元
 * @since JDK1.6
 */
class Tree {
/** 最优二叉树的根节点 */
protected TNode root;


/** 存储叶子节点的权值 */
protected List<Integer> leafWList = new ArrayList<Integer>();


/** 临时队列,用于存放待组合的节点 */
protected List<TNode> tmpList = new LinkedList<TNode>();


/** 存放带权节点 */
protected TNode[] leafArr = null;


/**
* 从键盘读取各个权值

*/
public void getLeafWeight() {
Scanner scan = new Scanner(System.in);


System.out.println("请输入各叶子节点的权值,0为结束:");


while (scan.hasNextInt()) {
int i = scan.nextInt();


if (i == 0)
break;
leafWList.add(new Integer(i));
}


scan = null;


return;
}


/**
* 找出临时队列中权值最小的节点从队列中移出并返回

* @return TNode 权值最小的节点
*/
public TNode min() {
Iterator<TNode> itr = tmpList.iterator();
TNode minNode = itr.next();
int min = minNode.getWeight();


// 找到最小的节点
TNode tmpNode;
while (itr.hasNext()) {
tmpNode = itr.next();
if (tmpNode.getWeight() < min) {
min = tmpNode.getWeight();
minNode = tmpNode;
}
}


// 最小的节点移出临时队列
tmpList.remove(minNode);


// 处理垃圾
itr = null;
tmpNode = null;


return minNode;


}


/**
* 根据权值创建叶子节点并加入临时队列

*/
public void makeLeafNode() {
leafArr = new TNode[leafWList.size()];


for (int i = 0; i < leafWList.size(); i++) {
TNode node = new TNode(leafWList.get(i).intValue());
leafArr[i] = node;
tmpList.add(node);
}
}


/**
* 根据权值构造最优的二叉树

*/
public void makeBestTree() {
// 根据权值创建叶子节点并加入临时队列
makeLeafNode();


TNode minNode1 = null, minNode2 = null;
TNode node = null;
// 构造最优树
while (tmpList.size() != 1) {
minNode1 = min();
minNode2 = min();


node = new TNode(minNode1.getWeight() + minNode2.getWeight(),
minNode1, minNode2);
tmpList.add(node);
}


root = node;


}


/**
* 先序遍历的递归调用

*/
protected void preT(TNode t) {
if (t.isLeaf()) {
System.out.print(t.getWeight() + " ");
return;
} else {
System.out.print(t.getWeight() + " ");
preT(t.lChild);
preT(t.rChild);
}
}


/**
* 先序遍历最优二叉树

*/
public void preOrderTraverse() {
preT(root);
}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 哈夫曼编码是一种使用变长编码来减少数据传输量的有效方法。在 Verilog 中,我们可以通过以下步骤实现哈夫曼编码。 首先,我们需要构建哈夫曼编码树。这棵树是由输入数据的频率构建而成的。可以使用哈夫曼树构建算法,该算法会根据输入数据的频率构造出最优的哈夫曼编码树。 然后,我们需要根据构建好的哈夫曼编码树生成对应的哈夫曼编码表。哈夫曼编码表将字符与对应的二进制编码一一对应起来。可以使用深度优先搜索的方法遍历哈夫曼编码树,生成哈夫曼编码表。 接下来,我们可以编写 Verilog 代码来实现哈夫曼编码。首先,我们需要定义输入数据的接口,并接收输入数据。然后,根据输入的字符,查询哈夫曼编码表,将对应的二进制编码输出。 最后,我们需要将输出的二进制编码进行传输。为了正确传输数据,我们需要在输出数据前加入标志位,表示输出数据的开始位置。在接收端,需要识别这个标志位,并将后续的二进制编码解码为对应的字符。 总结来说,哈夫曼编码在 Verilog 中的实现需要构建哈夫曼编码树,生成哈夫曼编码表,并编写相应的编码和解码逻辑。这样可以实现对输入数据的高效压缩和解压缩。 ### 回答2: 哈夫曼编码是一种基于字符频率来构建编码的最优前缀编码方法。在Verilog中实现哈夫曼编码可以分为两步:构建哈夫曼树和生成编码表。 首先,构建哈夫曼树。我们可以使用二叉树的数据结构来表示哈夫曼树。在Verilog中,可以通过定义一个节点结构体来表示二叉树节点,其中包括字符和频率信息,以及左右子节点指针。通过比较字符频率来构建哈夫曼树,可以采用贪心算法,每次选择频率最小的两个节点合并为一个新节点,直到只剩下一个节点为止。 接下来,生成编码表。通过遍历哈夫曼树,可以得到每个字符的编码。在Verilog中,可以使用递归或者迭代的方式进行树的遍历。当遍历到叶子节点时,记录下路径上的0和1,即可得到每个字符的哈夫曼编码。可以使用一个数据结构来保存字符与编码的对应关系,比如使用一个二维数组或者哈希表。 最后,将哈夫曼编码应用于实际数据压缩或传输中。通过将原始数据按照对应的编码进行替换或者添加额外的标识,可以实现压缩和解压缩的功能。 总之,通过Verilog实现哈夫曼编码需要先构建哈夫曼树,然后生成编码表,最后将编码应用于数据压缩或传输中。这是一个相对复杂的任务,需要熟悉Verilog语言和数据结构的相关知识才能完成。 ### 回答3: 哈夫曼编码是一种变长编码的压缩算法,常用于将频率较高的字符用较短的编码表示,从而减小存储或传输的数据量。为了实现哈夫曼编码,可以使用硬件描述语言Verilog来设计相应的电路。 实现哈夫曼编码的Verilog电路需要以下几个主要模块: 1. 频率统计模块:用于统计输入文本中各个字符的频率。输入文本可以通过数据输入端口传入,使用计数器来统计每个字符出现的次数。 2. 构建哈夫曼树模块:根据字符频率构建哈夫曼树。使用二叉堆等数据结构来优化树的构建过程,按照频率大小进行排序。 3. 哈夫曼编码生成模块:根据构建好的哈夫曼树,生成每个字符对应的哈夫曼编码串。可以使用递归或者迭代的方式遍历哈夫曼树,同时记录每个字符的编码。 4. 编码器模块:将输入的文本按照生成的哈夫曼编码进行编码。通过读取输入文本的每个字符,并查找对应的哈夫曼编码,输出对应的编码串。 5. 译码器模块:将编码后的二进制串按照哈夫曼编码进行译码,得到原始文本。根据哈夫曼编码树进行译码,从根节点开始依次查找对应的字符。 以上模块可以通过组合逻辑电路来实现,可以利用状态机等技术进行控制。此外,还需要提供测试模块,用于验证设计的正确性。 总的来说,通过使用Verilog语言来设计实现哈夫曼编码的电路,可以实现对输入文本进行压缩和解压缩的功能。这种硬件实现可以提高编码运行速度,并减小对系统资源的占用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值