题目: 哈夫曼编码大全
描述:
关于哈夫曼树的建立,编码,解码。
输入
第一行输入数字N,代表总共有多少个字符以及权值
第二第三行分别是一行字符串,以及每个字符对应的权值
接下来输入一个数M,表示接下来有M行字符串,要求你对每个字符串进行编码
再输入一个数X,表示接下来有X行编码,要求你对每行编码进行解码
输出
第一行输出所有节点的权重
接下来输出N行,每行以 “a:001”的格式输出每个字符对应的编码
接着输出M行,对输入的字符串的编码结果
最后,输出X行的解码结果
输入样例
6
abcdef
50 10 5 5 20 10
2
abcdef
defabaabbc
2
011001100100110110101101100
1100011000110101100101100
输出样例
50 10 5 5 20 10 10 20 30 50 100
a:0
b:100
c:1100
d:1101
e:111
f:101
010011001101111101
11011111010100001001001100
accbdfadb
cacadacfb
注意:哈夫曼编码不可能有相同的前缀
此题分三部分,构建,编码和解码
构建:构建哈夫曼树的过程即每次从储存的叶子结点中找到最小的两个叶子结点,重新组成一个新的叶子结点 ,最后长度不超过2*n-1
编码:首先获取编码,从底开始找到头,储存编码后,每次遍历找到相同字符,则输出对应编码
解码:相当于字符串匹配算法,找到相同字符串即可,输出编码对应的字符
import java.util.*;
public class Xingyuxingxi {
public static class jgt {
int qz, lc, rc, fq, dq;//权值,左孩子,右孩子,父结点,存储当前使用过的权值
public jgt() {
this.qz = 0;
this.lc = 0;
this.rc = 0;
this.fq = 0;
this.dq = 0;
}
}
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
String str=sc.next();
int m=2*n-1;
jgt [] tree=new jgt[m];
for (int i = 0; i < n; i++) {
tree[i]=new jgt();//每次都需要初始化
tree[i].qz=tree[i].dq=sc.nextInt();//dq中储存的是没有使用过的权值,因为一个结点只能使用一次
}
int x=n;
while(n!=m) {//最多只有m的长度
int min1 = 0,min2 = 0;//最小结点,和第二小结点
for (int j = 0; j < n; j++) {
if (tree[min1].dq > tree[j].dq) {//如果比当前下标的大,则将当前下标保存下来
min1 = j;
}
}
tree[min1].dq=Integer.MAX_VALUE;//如果被使用则赋值一个无限大的数表示已经使用
for (int j = 0; j < n; j++) {
if(tree[min2].dq>tree[j].dq&&min1!=j){//j不能等于min1因为已经存储了这个最小结点,只能使用一次
min2=j;
}
}
tree[n]=new jgt();//初始化,不初始化会空指针异常
tree[n].qz=tree[n].dq=tree[min1].qz+tree[min2].qz;
tree[n].lc=min1;//左孩子是最小叶子结点的下标,这里不像构造哈夫曼树一样+1,因为题目没有要求下标从1开始
tree[n].rc=min2;//右孩子是第二小叶子结点的下标
tree[min1].fq=tree[min2].fq=n;//左孩子和右孩子的父结点都是n
tree[min2].dq=Integer.MAX_VALUE;//如果被使用则赋值一个无限大的数表示已经使用
n++;//每次会产生一个新的结点
}
for (int i = 0; i < m; i++) {
System.out.print(tree[i].qz+" ");
}
System.out.println();
String []bm=new String[x];//储存编码
for (int i = 0; i < x; i++) {
String s="";
int c=i;//从该字符,也就是最底下的树叶开始往上找
int fq= tree[i].fq;//找到其父亲结点的序号
while(fq!=0) {//如果父亲结点为0代表找完了
if(tree[fq].lc==c) {//如果是左孩子则为0
s=s+"0";
}
else {//右孩子为1
s=s+"1";
}
c=fq;//然后让孩子结点变为父亲结点,即再往上找
fq=tree[fq].fq;//储存此时父亲结点的父亲结点序号
}
bm[i]="";
for (int j = s.length()-1; j >= 0; j--) {//因为是从下往上找,所以编码是反过来的,应该倒着赋值
bm[i]=bm[i]+s.charAt(j);
}
}
for (int i = 0; i < x; i++) {
System.out.println(str.charAt(i)+":"+bm[i]);
}
int q=sc.nextInt();
while(q--!=0) {
String s=sc.next();
for (int i = 0; i < s.length(); i++) {
for (int j = 0; j < x; j++) {
if(s.charAt(i)==str.charAt(j)) {//如果字符相同,则输出相应编码即可
System.out.print(bm[j]);
}
}
}
System.out.println();
}
q=sc.nextInt();
while(q--!=0) {
String s=sc.next();
for (int i = 0; i < s.length();) {
int y=i;//储存开始匹配的下标
for (int j = 0; j < x; j++) {
int cnt=0;//计数器
for (int k = 0; k < bm[j].length(); k++) {
if(bm[j].charAt(k)==s.charAt(i)) {//如果对应匹配则计数器+1,i也+1,继续往下看是否匹配
cnt++;
i++;
}
else {//如果不匹配就退出
break;
}
}
if(cnt==bm[j].length()) {//如果计数器的数值与编码的长度相同,则说明匹配成功,输出该编码的解码,即输出该字符
System.out.print(str.charAt(j));
break;//找到则跳出循环找下一部分
}
else {
i=y;//没找到则将i返回到y下标,重新从头开始匹配寻找
}
}
}
System.out.println();
}
}
}
觉得这一部分太长太难,可以看一下本专栏的:构造哈夫曼树(数据结构)(难度系数85)