20182323 哈夫曼编码测试
课程:《程序设计与数据结构》
班级: 1823
姓名: 曹骞
学号: 20182323
实验教师:王志强
实验日期:2019年10月21日
必修/选修: 必修
1.实验内容
设有字符集:S={a,b,c,d,e,f,g,h,i,j,k,l,m,n.o.p.q,r,s,t,u,v,w,x,y,z}。
给定一个包含26个英文字母的文件,统计每个字符出现的概率,根据计算的概率构造一颗哈夫曼树。
并完成对英文文件的编码和解码。
要求:
1.准备一个包含26个英文字母的英文文件(可以不包含标点符号等),统计各个字符的概率
2.构造哈夫曼树
3.对英文文件进行编码,输出一个编码后的文件
4.对编码文件进行解码,输出一个解码后的文件
2.实现过程
哈夫曼树的建立
(2). 从19,21,2,3,6,7,10,32中选择两个权小结点。选中2,3。同时算出这两个结点的和5。
(3). 从19,21,6,7,10,32,5中选出两个权小结点。选中
5,6。同时计算出它们的和11。
(4). 从19,21,7,10,32,11中选出两个权小结点。选中7,10。同时计算出它们的和17。(PS:这时选出的两个数字都不是已经构造好的二叉树里面的结点,所以要另外开一棵二叉树;或者说,如果两个数的和正好是下一步的两个最小数的其中的一个,那么这个树直接往上生长就可以了,如果这两个数的和比较大,不是下一步的两个最小数的其中一个,那么就并列生长。)
(5). 从19,21,32,11,17中选出两个权小结点。选中11,17。同时计算出它们的和28。
(6). 从19,21,32,28中选出两个权小结点。选中19,21。同时计算出它们的和40。另起一颗二叉树。
(7). 从32,28, 40中选出两个权小结点。选中28,32。同时计算出它们的和60。
(8). 从 40, 60中选出两个权小结点。选中40,60。同时计算出它们的和100。 好了,此时哈夫曼树已经构建好了。
以下为核心代码:
Node createTree(List nodes) {
while (nodes.size() > 1) {
quickSort(nodes);
Node left = nodes.get(nodes.size() - 1);
Node right = nodes.get(nodes.size() - 2);
Node parent = new Node(null, left.weight + right.weight);
parent.leftChild = left;
parent.rightChild = right;
nodes.remove(nodes.size() - 1);
nodes.remove(nodes.size() - 1);
nodes.add(parent);
}
return nodes.get(0);
- 以左为0,右为1,给字母编码
public void setCode(Node root) {
if (root.leftChild != null) {
root.leftChild.code = root.code + "0";
setCode(root.leftChild);
}
if (root.rightChild != null) {
root.rightChild.code = root.code + "1";
setCode(root.rightChild);
}
}
public class Node {
E data;
public String code = "";
double weight;
Node leftChild;
Node rightChild;
public Node(E data, double weight) {
super();
this.data = data;
this.weight = weight;
}
}
- 对英文文件进行编码
private String hfmCodeStr = "";
public String toHufmCode(String str,Node root) {
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i) ;
search(root, c);
}
return hfmCodeStr;
}
private void search(Node root, char c) {
if (root.leftChild == null && root.rightChild == null) {
if (c == (char)root.data) {
hfmCodeStr += root.code; // 找到字符,将其哈夫曼编码拼接到最终返回二进制字符串的后面
}
}
if (root.leftChild != null) {
search(root.leftChild, c);
}
if (root.rightChild != null) {
search(root.rightChild, c);
}
}
- 对编码文件进行解码
String result="";
boolean target = false;
public String CodeToString(String codeStr,Node root) {
int start = 0;
int end = 1;
while(end <= codeStr.length()){
target = false;
String s = codeStr.substring(start, end);
matchCode(root, s);
if(target){
start = end;
}
end++;
}
return result;
}
private void matchCode(Node root, String code){
if (root.leftChild == null && root.rightChild == null) {
if (code.equals(root.code)) {
result += root.data; // 找到对应的字符,拼接到解码字符穿后
target = true; // 标志置为true
}
}
if (root.leftChild != null) {
matchCode(root.leftChild, code);
}
if (root.rightChild != null) {
matchCode(root.rightChild, code);
}
}
其他(感悟、思考等)
要想进步,就只有吸取教训,成功的经验都是歪曲的,成功了,想怎么说都可以,失败者没有发言权,可是,你可以通过他的事例反思,总结。教训,不仅要从自己身上吸取,还要从别人身上吸取。
——马云