标签:blob 次数 ast 决策 遍历 进制 search 哈夫曼 第一个
解码:译码过程是分解、识别各个字符,还原数据的过程。从字符串头开始扫描到尾,依次去匹配。
首先构造结点类,每个结点需要有哈夫曼编码,字符本身,权值,以及左结点和右结点。
public class TreeNode {
public String code = "";// 节点的哈夫曼编码
public String data = "";// 节点的字符
public int count;// 节点的权值
public TreeNode lChild;//左结点
public TreeNode rChild;//右结点
public TreeNode(String data, int count) {
this.data = data;
this.count = count;
}
public TreeNode(int count, TreeNode lChild, TreeNode rChild) {
this.count = count;
this.lChild = lChild;
this.rChild = rChild;
}
}
计算每个字符的权重,flag用来判定取出的字符串是否存在,若不存在为true,如果存在那么就在出现次数统计上加一,即权重加一,如果没有出现过,就将它加入到存储字符的链表中。
// 统计出现的字符及出现次数
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);
// 遍历字符链表,若有相同字符则将个数加1
if(ch == data.chardata){
data.num++;
flag = false;
totalcount++;
break;
}
}
// 字符对象链表中没有相同字符则将其加到链表中
if(flag){
charList.add(new CharData(ch));
totalcount++;
}
}
}
将字符链表中的字符构建为结点,利用循环将字符链表中的字符和其权值创建为TreeNode结点,并加入到结点链表中。
//将字符创建为结点
private void creatNodes() {
for (int i = 0; i < charList.size(); i++) {
String data = charList.get(i).chardata + "";
int count = charList.get(i).num;//权值
TreeNode node = new TreeNode(data, count); // 创建节点对象
NodeList.add(node); // 加入到节点链表
}
}
构建哈弗曼树,根据上文提到的哈夫曼编码的规则,将最小的两个节点分别设置为左右子结点,左边编码为0,右边为1,然后依据父结点创建新的TreeNode结点,将新的结点放到链表首位并重新排序。一次循环进行,直到结点链表中的节点数不大于1,这时树就形成了。
//构建哈夫曼树
private void creatTree() {
// 当节点数目大于一时,将权值最小的两个节点生成一棵新树,父结点为两者之和,并删除两个结点将新树放到列表中
while (NodeList.size() > 1) {
TreeNode left = NodeList.poll();
TreeNode right = NodeList.poll();
// 设置各个结点的哈夫曼编码
left.code = "0";
right.code = "1";
setCode(left);
setCode(right);
int parentWeight = left.count + right.count;// 父节点权值等于子节点权值之和
TreeNode parent = new TreeNode(parentWeight, left, right);
NodeList.addFirst(parent); // 暂时将父节点置于首位
Sort(NodeList); // 重新排序将父结点放到合适位置
}
}
在构建哈夫曼树的过程中每次将最小的两个相加构造新结点时,需要对新的链表进行排序,所以用到升序排序法,将链表从左开始依次与其右边的所有字符比较权值,将权值小的字符放到左边,这样就形成升序链表。
//升序排序
private void Sort(LinkedList<TreeNode> nodelist) {
for (int i = 0; i < nodelist.size() - 1; i++) {
for (int j = i + 1; j < nodelist.size(); j++) {
TreeNode temp;
if (nodelist.get(i).count > nodelist.get(j).count) {
temp = nodelist.get(i);
nodelist.set(i, nodelist.get(j));
nodelist.set(j, temp);
}
}
}
}
设置每个结点的哈夫曼编码,从根结点开始,分别对做孩子和右孩子的编码添加0/1,左结点为0,右结点为1。这样每个叶子节点都有独一无二的01编码。
//设置结点的哈夫曼编码
private void setCode(TreeNode root) {
if (root.lChild != null) {
root.lChild.code = root.code + "0";
setCode(root.lChild);
}
if (root.rChild != null) {
root.rChild.code = root.code + "1";
setCode(root.rChild);
}
}
利用以上方法,根据哈夫曼编码的规则构建哈夫曼树
//构建哈夫曼树
public void creatHuffmanTree(String str) {
this.str = str;
NodeList = new LinkedList<TreeNode>();
charList = new LinkedList<CharData>();
// 统计字符串中字符以及字符的出现次数
getCharNum(str);
// 创建节点
creatNodes();
// 对节点升序排序
Sort(NodeList);
// 将权值最小的两个节点相加生成一个新的父节点并删除权值最小的两个节点,将父节点存放到列表中,形成树
creatTree();
// 将最后的一个节点赋给根节点
root = NodeList.get(0);
}
遍历节点输出字符的编码,通过判断左右孩子是否为空的情况,找到叶子结点,即字符,然后输出其编码和出现次数。
//遍历结点
private void output(TreeNode node) {
if (node.lChild == null && node.rChild == null) {
System.out.println(node.data + " 的编码为:" + node.code+ " 出现次数为:"+ node.count + " 出现概率为:"+ (node.count)/totalcount);
}
if (node.lChild != null) {
output(node.lChild);
}
if (node.rChild != null) {
output(node.rChild);
}
}
编码,定义字符串hfmCodeStr,将叶子结点的编码依次添加到编码字符串中,返回hfmCodeStr字符串。
//编码
public String creHufmCode(String str) {
for (int i = 0; i < str.length(); i++) {
String string = str.charAt(i) + "";
search(root, string);
}
return hfmCodeStr;
}
//找到叶子结点,添加其编码
private void search(TreeNode root, String c) {
if (root.lChild == null && root.rChild == null) {
if (c.equals(root.data)) {
hfmCodeStr += root.code;
}
}
if (root.lChild != null) {
search(root.lChild, c);
}
if (root.rChild != null) {
search(root.rChild, c);
}
}
解码,通过遍历叶子结点找到相匹配的字符编码,定义编码后的字符串从第一个数字开始,用substring方法截取字符串,如果找到相匹配的叶子结点编码,就进行解码,当解码失败时,说明编码不匹配,end向后移,当解码成功时,first向后移,对下一段字符进行解码,最后输出解码后的字符串。
//解码
public String decode(String codeStr) {
int first = 0;
int end = 1;
while(end <= codeStr.length()){
target = false;
String s = codeStr.substring(first, end);
matchCode(root, s); // 解码
// 每解码一个字符,first向后移
if(target){
first = end;
}
end++;
}
return result;
}
//匹配字符哈夫曼编码,找到对应的字符
private void matchCode(TreeNode root, String code){
if (root.lChild == null && root.rChild == null) {
if (code.equals(root.code)) {
result += root.data; // 找到对应的字符,拼接到解码字符串后
target = true; // 标志为true
}
}
if (root.lChild != null) {
matchCode(root.lChild, code);
}
if (root.rChild != null) {
matchCode(root.rChild, code);
}
}
读取文件中的字符串,并构造哈夫曼树
BufferedReader a = new BufferedReader(new FileReader("wen.txt"));
String data = a.readLine();
huff.creatHuffmanTree(data);// 构造树
将编码解码后的内容写入文件
//写入文件
File file = new File("wen2");
Writer write = new FileWriter(file);
write.write(hufmCode);
write.flush();
write.write(huff.decode(hufmCode));
write.close(
将编码解码后的结果存入文件
https://gitee.com/CS-IMIS-23/20172314/blob/master/src/新第十四周/Huffman.java
标签:blob 次数 ast 决策 遍历 进制 search 哈夫曼 第一个
原文地址:https://www.cnblogs.com/YiYiYi/p/10091764.html