赫夫曼树编码(Java)
赫夫曼编码(Huffman Coding),又称哈夫曼编码,是一种编码方式,赫夫曼编码是可变字长编码(VLC)的一种。Huffman于1952年提出一种编码方法,该方法完全依据字符出现概率来构造异字头的平均长度最短的码字,有时称之为最佳编码,一般就叫做Huffman编码。
- 编码:在数据通讯中,经常需要将传送的文字转换成由二进制字符0 ,1组成的二进制串
- 赫夫曼编码: 赫夫曼树中的 左分支代表0 , 右分支代表1 ,这样从根结点到每个叶子结点所经过的路径分支组成的0 和1 的序列便为该结点对应字符的编码,我们称之为 赫夫曼编码 。
- 在赫夫曼编码树中,树的带权路径长度的含义是各个字符的码长与其出现次数的乘积之和,也就是电文的代码总长,所以 采用赫夫曼树构造的编码是一种能使电文代码总长最短的不等长编码
- 要求:任一字符的编码都不能是另一字符编码的前缀
- 实际应用中会根据各字符的出现频度,用 短 ( 长 )编码表示频率 大 ( 小 )的字符以使得编码序列的总长度最小
- 赫夫曼编码并不唯一,所得到的编码长度也不一定一致,
但带权路径长度一定相同,一定是最小的
代码如下
参考代码:https://blog.csdn.net/qq_34975710/article/details/78563461
import java.util.LinkedList;
import java.util.Scanner;
public class Huffmancode {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
Huffman huff = new Huffman();
System.out.println("请输入你要设计赫夫曼编码的字符串:");
String data =sc.nextLine();
huff.creathuffmantree(data);
System.out.println("各字符的赫夫曼编码为:");
huff.output(); // 显示字符的赫夫曼编码
String huffmancode = huff.tohuffmancode(data);
System.out.println("编码结果为:" + huffmancode);
// 将上述二进制编码再翻译成字符串
System.out.println("解码结果为:" + huff.CodeToString(huffmancode));
}
public static class Huffman {
String str;// 最初用于压缩的字符串
Node root;// 赫夫曼二叉树的根节点
boolean flag;// 最新的字符是否已经存在的标签
LinkedList<CharData> charList;// 存储不同字符的队列 相同字符存在同一位置
LinkedList<Node> NodeList;// 存储节点的队列
private class CharData {
int num = 1; // 字符个数
char c; // 字符
public CharData(char ch){
c = ch;
}
}
public void creathuffmantree(String str) {
this.str = str;
NodeList = new LinkedList<Node>();
charList = new LinkedList<CharData>();
getCharNum(str);
creatNodes();
Sort(NodeList);
creatTree();
root = NodeList.get(0);
}
private void getCharNum(String str) {
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i); // 从给定的字符串中取出字符
flag = true;
for (int j = 0; j < charList.size(); j++) {
CharData data = charList.get(j);
if(ch == data.c){
data.num++;
flag = false;
break;
}
}
if(flag){
charList.add(new CharData(ch));
}
}
}
private void creatNodes() {//将出现的字符创建成单个的结点对象
for (int i = 0; i < charList.size(); i++) {
String data = charList.get(i).c + "";
int count = charList.get(i).num;
Node node = new Node(data, count); // 创建节点对象
NodeList.add(node); // 加入到节点链表
}
}
private void creatTree() {//构建赫夫曼树
while (NodeList.size() > 1) {// 当节点数目大于一时
Node left = NodeList.poll();
Node right = NodeList.poll();
left.code = "0";
right.code = "1";
setCode(left);
setCode(right);
int parentWeight = left.count + right.count;// 父节点权值等于子节点权值之和
Node parent = new Node(parentWeight, left, right);
NodeList.addFirst(parent); // 将父节点置于首位
Sort(NodeList); // 重新排序,避免新节点权值大于链表首个结点的权值
}
}
private void Sort(LinkedList<Node> nodelist) {
for (int i = 0; i < nodelist.size() - 1; i++) {
for (int j = i + 1; j < nodelist.size(); j++) {
Node temp;
if (nodelist.get(i).count > nodelist.get(j).count) {
temp = nodelist.get(i);
nodelist.set(i, nodelist.get(j));
nodelist.set(j, temp);
}
}
}
}
private void setCode(Node root) {//设置结点的赫夫曼编码
if (root.left != null) {
root.left.code = root.code + "0";
setCode(root.left);
}
if (root.right != null) {
root.right.code = root.code + "1";
setCode(root.right);
}
}
private void output(Node node) {//遍历输出结点
if (node.left == null && node.right == null) {
System.out.println(node.data + ": " + node.code);
}
if (node.left != null) {
output(node.left);
}
if (node.right != null) {
output(node.right);
}
}
public void output() {//输出赫夫曼编码
output(root);
}
//解码
private String hfmCodeStr = "";// 赫夫曼编码连接成的字符串
public String tohuffmancode(String str) {
for (int i = 0; i < str.length(); i++) {
String c = str.charAt(i) + "";
search(root, c);
}
return hfmCodeStr;
}
private void search(Node root, String c) {
if (root.left == null && root.right == null) {
if (c.equals(root.data)) {
hfmCodeStr += root.code; // 找到字符,将其赫夫曼编码拼接到最终返回二进制字符串的后面
}
}
if (root.left != null) {
search(root.left, c);
}
if (root.right != null) {
search(root.right, c);
}
}
// 保存解码的字符串
String result="";
boolean target = false; // 解码标记
public String CodeToString(String codeStr) {
int start = 0;
int end = 1;
while(end <= codeStr.length()){
target = false;
String s = codeStr.substring(start, end);
matchCode(root, s); // 解码
// 每解码一个字符,start向后移
if(target){
start = end;
}
end++;
}
return result;
}
private void matchCode(Node root, String code){//匹配字符赫夫曼编码,找到对应的字符
if (root.left == null && root.right == null) {
if (code.equals(root.code)) {
result += root.data; // 找到对应的字符,拼接到解码字符穿后
target = true; // 标志置为true
}
}
if (root.left != null) {
matchCode(root.left, code);
}
if (root.right != null) {
matchCode(root.right, code);
}
}
}
}
class Node {
String code = "";// 节点的赫夫曼编码
String data = "";// 节点的数据
int count;// 节点的权值
Node left;
Node right;
public Node() {
}
public Node(String data, int count) {
this.data = data;
this.count = count;
}
public Node(int count, Node left, Node right) {
this.count = count;
this.left = left;
this.right = right;
}
public Node(String data, int count, Node left, Node right) {
this.data = data;
this.count = count;
this.left = left;
this.right = right;
}
}
请输入你要设计赫夫曼编码的字符串:
我喜欢写代码
各字符的赫夫曼编码为:
我: 00
喜: 01
代: 100
码: 101
欢: 110
写: 111
编码结果为:0001110111100101
解码结果为:我喜欢写代码