哈夫曼树─即最优二叉树,带权路径长度最小的二叉树,经常应用于数据压缩。通过将高频率字符用较短字节进行表示而不是统一表示的方法达到字符串的压缩。
啥是带权路径?- 每一个结点代表着不同的数字,而这些不同的数字称作结点的权值。
- 二叉树中一个结点到另一个结点经过的结点方式是唯一的,这些经过的结点称作两个结点之间的路径。而由结点A到结点B中间经历的结点数目加1即为结点A到B的路径长度。
哈夫曼为啥是前缀编码?
因为**定长编码已经用相同的位数这个条件保证了任一个字符的编码都不会成为其它编码的前缀,**所以这种情况只会出现在变长编码当中,要想避免这种情况,我们就必须用一个条件来制约定长编码,这个条件就是要想成为压缩编码,变长编码就必须是前缀编码.什么是前缀编码呢?所谓的前缀编码就是任何一个字符的编码都不能是另一个字符编码的前缀。假如有那扫描到前缀,就没法确定是哪个。
正式代码:
package Huffman;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class HaffmanTree {
public static void main(String[] args) {
String str = "ajsfhsdhfasdfkjhsdfhsalkdjsfhdsfhjsklasjfjksfdghkslkdahjfjhsdgasjfhsdjfjshhfg";
//1.统计出现次数,就是权重,map存的是字符及对应权重
HashMap<Character,Integer> map=new HashMap<>();
for(int i=0;i<str.length();++i){
Character ch=str.charAt(i);
if(map.get(ch)==null){
map.put(ch,1);
}else{
map.put(ch,map.get(ch)+1);
}
}
//2.树节点获取map字符和出现次数,加入列表
ArrayList< HuffmanNode> arr=new ArrayList<>();
for(Map.Entry<Character,Integer> en:map.entrySet()){
//System.out.println("key:"+en.getKey()+"value"+en.getValue());
HuffmanNode node=new HuffmanNode(en.getKey(),en.getValue());
arr.add(node);
}
//最小两个加入
while(arr.size()>1){
HuffmanNode[]data=getNode(arr);
//这就是创建的新节点
HuffmanNode root=new HuffmanNode(null,data[0].getNum()+data[1].getNum());
root.setLeft(data[0]);
root.setRight(data[1]);
//新节点加入节点表
arr.add(root);
}
//构建成功
HuffmanNode tree=arr.get(0);
//System.out.println(tree);//最终Haffman树
//编码
Map<Character,String> charMap=new HashMap<>();
Map<String,Character> codeMaps=new HashMap<>();
allViewMap(tree,"",charMap,codeMaps);
String hafucode="";
for(int i=0;i<str.length();++i){
Character ch=str.charAt(i);
hafucode+= charMap.get(ch);
}
System.out.println(hafucode.length()+"||"+str.length());
System.out.println(hafucode);
//先序遍历
//allView(tree);
// allViewCode(tree,"");
String s=decode(hafucode,charMap);
System.out.println("反编:"+s);
}
public static HuffmanNode []getNode(ArrayList<HuffmanNode>arr){
HuffmanNode[]nodes=new HuffmanNode[2];
int index1 ;
int index2;
if (arr.get(0).getNum() <= arr.get(1).getNum() ){
index1=0;
index2=1;
}else{
index1=1;
index2=0;
}
//只是通过index取数据,通过取出的数据比较
//然后
for(int i=2;i<arr.size();++i){
//当前比min1小
if(arr.get(i).getNum()<arr.get(index1).getNum()){
//先换index2
index2=index1;
index1=i;
//当前比min1大比min2小
//比min2大就不用换了
}else if(arr.get(i).getNum()>=arr.get(index1).getNum() && arr.get(i).getNum()<=arr.get(index2).getNum()){
index2=i;
}
}
nodes[0]=arr.get(index1);
nodes[1]=arr.get(index2);
arr.remove(index1);
//若index2删除前,index1删除了,remove(index2-1),因为index的缺失数组下标统一前移了
//
if(index2>index1){
arr.remove(index2-1);
}else{
arr.remove(index2);
}
return nodes;
}
public static void treeShow( HuffmanNode tree){
System.out.println();
}
// 先序遍历
public static void allView( HuffmanNode tree){
///可以不判断空,最优树,根节点必不为空
if(tree==null){
return;
}else{
if(tree.getLeft()==null&&tree.getRight()==null){
System.out.println(tree.getNum()+" || "+tree.getCh());
}else{
allView(tree.getLeft());
allView(tree.getRight());
}
}
}
public static void allViewCode( HuffmanNode tree,String code){
///可以不判断空,最优树,根节点必不为空
if(tree==null){
return;
}else{
if(tree.getLeft()==null&&tree.getRight()==null){
System.out.println(tree.getNum()+" || "+tree.getCh()+"||"+code);
}else{
allViewCode(tree.getLeft(),code+"0");
allViewCode(tree.getRight(),code+"1");
}
}
}
//编码(由遍历而来的)
public static void allViewMap( HuffmanNode tree,String code,Map<Character,String> charMaps,Map<String,Character> codeMaps){
///可以不判断空,最优树,根节点必不为空
if(tree==null){
return;
}else{
if(tree.getLeft()==null&&tree.getRight()==null){
// System.out.println(tree.getNum()+" || "+tree.getCh()+"||"+code);
charMaps.put(tree.getCh(),code);
codeMaps.put(code,tree.getCh());
}else{
allViewMap(tree.getLeft(),code+"0",charMaps,codeMaps);
allViewMap(tree.getRight(),code+"1",charMaps,codeMaps);
}
}
}
//解码
public static String decode(String str,Map<Character,String> map){
String s="";
while(str.length()>0) {
for (Map.Entry<Character, String> entry : map.entrySet()) {
if (str.startsWith(entry.getValue())) {
s += entry.getKey();
str = str.substring(entry.getValue().length());
break;
}
}
}
return s;
}
}
package Huffman;
public class HuffmanNode{
private Character ch;
private int num;
public HuffmanNode getLeft() {
return left;
}
public void setLeft(HuffmanNode left) {
this.left = left;
}
public HuffmanNode getRight() {
return right;
}
public void setRight(HuffmanNode right) {
this.right = right;
}
private HuffmanNode left;
private HuffmanNode right;
public HuffmanNode() {
}
public HuffmanNode(Character ch, int num) {
this.ch = ch;
this.num = num;
}
public Character getCh() {
return ch;
}
public void setCh(Character ch) {
this.ch = ch;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
@Override
public String toString() {
return "HuffmanNode{" +
"ch=" + ch +
", num=" + num +
", left=" + left +
", right=" + right +
'}';
}
}