package org.rjb.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Scanner;
/**
* 通过输入元素的权值计算出该元素的赫夫曼编码
* @author LovinChan
* @version 1.2
*
* 要不是一个我也不知道什么名字的实验课,我也没想到自己动手写这么个丑程序,开始写的
* 时候还真的遇到了一些困难,主要是对赫夫曼树的数据结构不熟悉,所以刚开始写这个程序的
* 时候还真的动了一番脑筋,也在网上下了一些代码看了看,可是用我自己的数据测试的时候发
* 现那些程序很多还是有问题的,于是乎悲剧性的我决定自己写这么个程序,通过输入元素的权
* 值计算该元素的赫夫曼编码。
* 权值暂时只能用整数,因为写得比较赶,可能很多地方都没考虑到,希望大家如果发现了错
* 误能给小弟指正出来,3KS 啦,如果想要跟我交流可以加我的 QQ 或者 MSN:
* MSN: egg.chenlw@gmail.com
* QQ: 372133556(请注明为什么加我)
* 一个小小的赫夫曼编码Java版算法,希望得到大虾们的点评指导。
*
*/
public class HuffmanCode {
// main method
public static void main(String[] args) {
System.out.println("*************** 程序开始 ***************");
HuffmanCode test = new HuffmanCode();
// test.test(); 测试方法
test.getNodeValue();
test.caculate();
test.result();
System.out.println("*************** 程序结束 ***************");
}
/**
* 在命令行获取结点元素值和它的权值
*/
public void getNodeValue() {
// 通过 Scanner 从命令行获得这些值
Scanner scanner = new Scanner(System.in);
boolean flag = true;
while (flag) {
System.out.println("请输入结点值: (输入 \"-q\" 退出)");
String value = scanner.next();
// 输入 -q ,则结束输入
if (value.equals("-q")) {
flag = false;
System.out.println(nodes.size() + " 个结点待编码");
scanner.close();
return;
} else {
System.out.println("请输入结点权值(整数): ");
// 权值必须暂必须为整数
int authority = scanner.nextInt();
// 构造结点对象
Node node = new Node(value,authority, "", null, null, null);
// 添加进待排序结点链表中
nodes.add(node);
}
}
}
/**
* 获得输出结果
* 结果有输入结点元素值,权值和它的赫夫曼编码,以及平均码长
*/
public void result() {
int average = 0;
for (Node node : sortedNodes) {
System.out.println(node);
average = average + (node.authority) + node.huffmanCode.length();
}
System.out.println("平均码长: " + average);
}
/**
* 先序遍历已排好的赫夫曼树,并把所有的结点加入到一个链表中
* @param node 根结点
*/
private void preOrder(Node node) {
if (null != node) {
nodes.add(node);
// 递归算法
preOrder(node.leftChild);
preOrder(node.rightChild);
}
}
/**
* 计算排好序的赫夫曼树中叶结点的赫夫曼编码,也就是用户输入的结点元素的赫夫曼编码
* @param nodes 排好序的赫夫曼树
*/
private void setHuffmanCode(List<Node> nodes) {
for (Node node : nodes) {
if (null != node.value) {
// 对每个叶结点,往上遍历到根结点,并顺序记录每个结点的 huffmanCode
// 最后得到的 huffmanCode 就是该叶结点的赫夫曼编码
Node parent = node.parent;
String huffmanCode = node.huffmanCode;
while (null != parent) {
huffmanCode = huffmanCode + parent.huffmanCode;
parent = parent.parent;
}
node.huffmanCode = huffmanCode;
// 将叶结点放到另一个链表中存储起来
sortedNodes.add(node);
}
}
}
/**
* 将用户输入的元素排列起来,并设计一个赫夫曼树,将根结点存到 unsortedNodes 链表中
* 然后通过先序遍历根结点,将每一个结点都存到该链表中,再提取链表中的叶结点,计算得叶
* 结点的赫夫曼编码,转存叶结点。
*/
public void caculate() {
// nodes 中只剩下一个结点,也就是根结点咯...
if (nodes.size() == 1) {
Node node = nodes.get(0);
// 清空 nodes 链表,删除 nodes 中最后一个元素(根结点)
nodes.remove(0);
preOrder(node);
setHuffmanCode(nodes);
return;
}
// 先将传入结点链表按权值排序
Collections.sort(nodes, new Node());
// 取出前两个元素作为新结点的左右子结点,然后从链表中移除这两个元素
Node leftChild = nodes.get(0);
Node rightChild = nodes.get(1);
// 左子树叶子结点总是用户输入的元素之一
if (null == leftChild.value && null != rightChild.value) {
Node temp = leftChild;
leftChild = rightChild;
rightChild = temp;
}
nodes.remove(0);
nodes.remove(0);
/************************ 开始计算 ************************/
/** 排序结果计算每个传入结点的霍夫曼编码 **/
int authority = leftChild.authority + rightChild.authority;
// 构造父结点,权值为左右子结点的权值和
Node parent = new Node(null, authority, "", leftChild, rightChild, null);
// 设置左右子结点的 huffmanCode ,左结点为 "0";右结点为 "1"
leftChild.huffmanCode = leftChild.huffmanCode + "0";
rightChild.huffmanCode = rightChild.huffmanCode + "1";
// 将子结点的父结点引用指向新创造的父结点
leftChild.parent = parent;
rightChild.parent = parent;
nodes.add(parent);
caculate();
}
private List<Node> nodes = new ArrayList<Node>();
public List<Node> sortedNodes = new ArrayList<Node>();
/**
* 结点描述
* @author LovinChan
* @version 1.5
*/
class Node implements Comparator<Node> {
public Node() {}
public Node( String value, int authority, String code, Node leftChild, Node rightChild, Node parent) {
this.huffmanCode = code;
this.authority = authority;
this.value = value;
this.leftChild = leftChild;
this.rightChild = rightChild;
this.parent = parent;
}
// huffmanCode
String huffmanCode;
// 权值
int authority;
// 值
String value;
// 左孩子
Node leftChild;
// 右孩子
Node rightChild;
// 父结点
Node parent;
public String toString() {
return "元素值: " + value
+ "\n权值: " + authority
+ "\nHuffman code: " + huffmanCode ;
}
@Override // 便于对 List 排序
public int compare(Node o1, Node o2) {
if (o1.authority > o2.authority) {
return 1;
} else if (o1.authority == o2.authority) {
if (null == o1.parent && null == o2.parent) {
return 0;
} else if (null != o1.parent && null == o2.parent) {
return -1;
} else if (null == o1.parent && null != o2.parent) {
return 1;
} else {
return 0;
}
} else {
return -1;
}
}
}
}
/** 测试数据....
* *************** 程序开始 ***************
请输入结点值: (输入 "-q" 退出)
a
请输入结点权值(整数):
1
请输入结点值: (输入 "-q" 退出)
b
请输入结点权值(整数):
2
请输入结点值: (输入 "-q" 退出)
c
请输入结点权值(整数):
3
请输入结点值: (输入 "-q" 退出)
d
请输入结点权值(整数):
4
请输入结点值: (输入 "-q" 退出)
e
请输入结点权值(整数):
5
请输入结点值: (输入 "-q" 退出)
f
请输入结点权值(整数):
6
请输入结点值: (输入 "-q" 退出)
g
请输入结点权值(整数):
7
请输入结点值: (输入 "-q" 退出)
-q
********** 程序运行结果
**********7 个结点待编码
元素值: f
权值: 6
Huffman code: 00
元素值: c
权值: 3
Huffman code: 010
元素值: a
权值: 1
Huffman code: 0110
元素值: b
权值: 2
Huffman code: 1110
元素值: g
权值: 7
Huffman code: 01
元素值: d
权值: 4
Huffman code: 011
元素值: e
权值: 5
Huffman code: 111
平均码长: 7
*************** 程序结束 ***************
*/
/*
public void test() {
Node a = new Node( "a", 5, "", null, null, null);
Node b = new Node( "b", 29, "", null, null, null);
Node c = new Node( "c", 7, "", null, null, null);
Node d = new Node( "d", 8, "", null, null, null);
Node e = new Node( "e", 14, "", null, null, null);
Node f = new Node( "f", 23, "", null, null, null);
Node g = new Node( "g", 3, "", null, null, null);
Node h = new Node( "g", 11, "", null, null, null);
unsortedNodes.add(a);
unsortedNodes.add(b);
unsortedNodes.add(c);
unsortedNodes.add(d);
unsortedNodes.add(e);
unsortedNodes.add(f);
unsortedNodes.add(g);
unsortedNodes.add(h);
System.out.println(unsortedNodes.size());
caculate();
int average = 0;
for (Node node : sortedNodes) {
System.out.println(node);
average = average + (node.authority) + node.huffmanCode.length();
}
System.out.println("平均码长: " + average / sortedNodes.size());
} */
分享到:
相关推荐
赫夫曼编码是一种高效的数据压缩方法,主要用于无损数据压缩,是数据结构中重要的概念之一。这个主题与著名计算机科学家严蔚敏教授有关,他在数据结构领域的教学和研究上有着深厚的影响力。赫夫曼编码的基本思想是...
在“赫夫曼编码译码器”这个小程序中,我们可以理解它具备两个主要功能:编码和解码。编码过程涉及以下步骤: 1. **建立赫夫曼树**:首先,根据输入文件中各个字符的出现频率,创建一个二叉树结构,称为赫夫曼树。...
赫夫曼编码是一种高效的数据压缩方法,由大卫·赫夫曼在1952年提出。它是基于一种称为“赫夫曼树”(Huffman Tree)的数据结构,用于创建一个可变长度的二进制码,使得频繁出现的字符具有较短的编码,而不常出现的...
通过阅读和理解这部分内容,你可以深入理解赫夫曼编码的工作原理,并能够编写自己的赫夫曼编码程序。 在实际应用中,赫夫曼编码常被用于文件压缩软件,如早期的LZ77、LZ78压缩算法以及后来的DEFLATE算法(广泛应用...
《构建与理解赫夫曼编码程序》 赫夫曼编码是一种高效的无损数据压缩方法,由赫尔曼·赫夫曼在1952年提出。它的核心思想是利用频率较低的字符用较短的编码,频率较高的字符用较长的编码,从而在总体上减少编码的平均...
这个实验不仅涵盖了数据结构和算法的知识,还涉及了程序设计和控制台交互,对于理解赫夫曼编码原理和实现有很好的实践意义。通过亲手实现,你可以深入理解赫夫曼编码的每一个环节,并且掌握如何在有限的资源下,如...
本次实验的目标是对一篇大约500单词的英文文本文件中的所有字母和标点符号进行统计,计算它们出现的频率,并基于这些频率构建赫夫曼树,进而生成赫夫曼编码。 #### 二、需求分析 根据题目要求,可以将整个项目的...
5. **性能分析**:实验报告还会对比压缩前后的数据大小,计算压缩比,以证明赫夫曼编码的有效性。此外,可能会讨论时间复杂度和空间复杂度,分析算法的效率。 6. **实验结果与讨论**:这部分会展示实验的具体结果,...
在“SFE.rar_sfe_赫夫曼编码”这个压缩包中,包含的SFE.txt文件很可能是程序或数据的示例,用于演示如何实现赫夫曼编码。可能的内容包括字符频率表、赫夫曼树的结构或者压缩与解压缩的代码实现。 五、编程实现 在...
- 在这个项目中,可能有一个名为“赫夫曼编码(通过修改txt文件获取不同的编码)”的程序,它可能读取txt文件,计算字符频率,然后根据这些频率动态生成赫夫曼树和相应的编码。 3. **Xcode的使用**: - Xcode是...
3. 遍历所有叶子节点,并计算每个叶子节点的赫夫曼编码,同时将编码结果写入文件。 4. 输出每个字符的赫夫曼编码。 5. 对整个字符串进行编码,并显示编码后的结果。 6. 调用 `unhanffman` 函数解码编码后的字符串,...
在数据库课程设计中,我们可以设计一个数据库来存储字符及其频率信息,以便计算和存储赫夫曼编码。此外,还需要实现编码和解码两个功能。编码过程涉及遍历赫夫曼树,根据左分支记为0,右分支记为1,将字符转换成二...
最后,程序会打印出每个字符的赫夫曼编码,以及赫夫曼树中各节点的详细信息,包括节点的权重、父节点索引和左右孩子索引。 在实际应用中,赫夫曼编码被广泛应用于文件压缩、网络数据传输等领域。例如,著名的ZIP...
3. **生成赫夫曼编码**:从根节点遍历到每个叶子节点,记录路径(左代表0,右代表1),得到每个字符的赫夫曼编码。 4. **编码文本**:根据生成的赫夫曼编码,将文本中的每个字符转换为对应的二进制序列。 解码器...
总之,赫夫曼树和赫夫曼编码是数据压缩领域的基础工具,它们通过优化编码长度来提高压缩效率,是理解信息论和数据压缩原理的关键概念。在`Huffman.cpp`代码中,我们可以深入学习如何通过编程实现这一过程。
在学习这些源码时,可以深入了解如何在实际程序中实现赫夫曼编码和解码的细节,如数据结构的使用(如优先队列)、位操作以及如何有效地存储和重建赫夫曼树。这不仅可以提高编程技能,还能对数据压缩理论有更深入的...
总的来说,用赫夫曼编码完成文件压缩涉及到了数据结构(如二叉树)、编码理论(赫夫曼编码)、文件处理和程序设计等多个方面的知识。通过这一过程,可以有效地减少文件占用的存储空间,提高传输效率。