Bootstrap

用java编写哈夫曼树,并实现解码与编码

(java哈夫曼树)用链式结构对字符串的读取,并编码,从而实现编译跟反编译.

思路:

1.获取文本的数据的时候,用一个容器来存放出现的字符跟频率,这里用了hashmap
2.把函数里面的数据赋值给相应链表的节点,然后用链表的排序,每次都把数据拿出来,赋值给一个新的节点,再放回去,实现了最后建树
3.递归遍历树,给树的节点赋值哈夫曼编码,每次都遍历到叶节点,而返回上一层的时候,需要记住把哈夫曼编码删除最后一位,实现返回上一层
4.编译跟反编译,就是遍历搜索树,因为我们自己输入的哈夫曼编码可能不在我们的树里,所以,只用输入文本.
5.改进了上面的代码,我们可以利用输入的01字符串,实现解码(通过遍历哈夫曼树)

建立链表

/**
 * Create on 2021/4/19
* author:chen
 */
class ListNode
{
    HuffmanNode data;   //存放哈夫曼节点
    ListNode next;

    ListNode()
    {
        this.data = null;
        this.next = null;
    }

    ListNode(HuffmanNode hnode)
    {
        this.data = hnode;
        this.next = null;
    }

    HuffmanNode getData()
    {
        return data;
    }
}

哈夫曼节点

/**
 * Create on 2021/4/19
* author:chen
 */

class HuffmanNode implements Comparable <HuffmanNode>
{
    char thechar;   //存放字符
    float weight;   //存放权重
    int height;     //存放高度
    HuffmanNode left;   //左节点
    HuffmanNode right;  //右节点
    StringBuilder huffmanCode;   //哈夫曼编码

    //初始化节点
    HuffmanNode()
    {
        this.thechar = '\0';
        this.weight = 0.0f;
        this.height = 0;
        this.left = null;
        this.right = null;
        this.huffmanCode = null;
    }

    //给节点赋值char,跟weight
    HuffmanNode(char achar, float weight)
    {
        this.thechar = achar;
        this.weight = weight;
        this.height = 0;
        this.left = null;
        this.right = null;
        this.huffmanCode = null;
    }

    public void setWeight(float weight) {
        this.weight = weight;
    }

    void setHeight(int height)
    {
        this.height = height;
    }
    float getWeight()
    {
        return this.weight;
    }

    int getHeight()
    {
        return this.height;
    }

    void setLeftChild(HuffmanNode node)
    {
        this.left = node;
    }

    void setRightChild(HuffmanNode node)
    {
        this.right = node;
    }
    HuffmanNode getLeftChild()
    {
        return this.left;
    }
    HuffmanNode getRightChild()
    {
        return this.right;
    }
    char getChar()
    {
        return thechar;
    }

    String getHuffmanCode()
    {
        if (this.huffmanCode == null)
            return "";
        else
            return this.huffmanCode.toString();
    }
    void setHuffmanCode(StringBuilder code)
    {
        this.huffmanCode = new StringBuilder(code);
    }

    //重写compareTo比较方法,比较权重,权重一样,则比较节点高度
    public int compareTo(HuffmanNode node)
    {
        int val = 1;
        if (this.getWeight() > node.getWeight())
            return val;
        else if (this.getWeight() < node.getWeight())
            val = -1;
        else
        {
            if (this.getChar()!='\0'){
                if (this.getChar()>this.getChar())
                    return val;
                else if (this.getChar() < node.getChar())
                    val = -1;
            }

        }
        return val;
    }

    @Override
    public String toString() {
        return "HuffmanNode{" +
                "thechar=" + thechar +
                ", weight=" + weight +
                ", left=" + left +
                ", right=" + right +
                ", huffmanCode=" + huffmanCode +
                '}';
    }
}

存放哈夫曼编码的链表

/**
 * Create on 2021/4/19
* author:chen
 */

public class HuffmanList
{
    ListNode head;
    int len;

    HuffmanList()
    {
        head = new ListNode();
        len = 0;
    }

    //在头节点后面添加一个节点
    void addNode(HuffmanNode hnode)
    {
        ListNode lnode = new ListNode(hnode);
        lnode.next = head.next;
        head.next = lnode;
        len++;
    }

//    HuffmanNode getMin()
//    {
//        ListNode p = head;
//        ListNode min = p;
//        HuffmanNode minnode = p.next.getData();
//
//        while (p.next != null)
//        {
//            if (p.next.getData().compareTo(minnode) <= 0)
//            {
//                min = p;
//                minnode = min.next.getData();
//            }
//            p = p.next;
//        }
//        // 删除节点
//        min.next = min.next.next;
//        len--;
//        return minnode;
//    }

    //获取第一个节点,并删除
    HuffmanNode getFirstNode()
    {
        HuffmanNode p = head.next.getData();
        head.next = head.next.next;
        len--;
        return p;
    }

    int getlen()
    {
        return len;
    }

    //输出链表
    void output(HuffmanList list){
        ListNode node=list.head.next;
        while (node!=null){
            System.out.println(node.getData().toString());
            node=node.next;
        }
    }
}


哈夫曼树(主要方法都在这里面)

import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.InputStream;
import java.util.*;

/**
 * Create on 2021/4/19
 * author:zhenghuanxin
 */
public class HuffmanTree {
    HuffmanList HList=new HuffmanList();

    //把map里面的东西存进链表里面
    HuffmanList addHlist(Map<Character, Float> txtMap){
        for(Object key:txtMap.keySet()){
            HList.addNode(new HuffmanNode((char)key, txtMap.get(key)));
        }
//        HList.output(HList);
//        HuffmanList newList=sortedHlist(HList);
//        newList.output(newList);
//        return newList;
        return HList;
    }

    //对链表进行冒泡排序
    HuffmanList sortedHlist(HuffmanList HList){
        int len=HList.getlen();
        for (int i=0;i<len;i++){
            ListNode tmp=HList.head;
            while (tmp!=null&&tmp.next!=null&&tmp.next.next!=null){
                if (tmp.next.getData().compareTo(tmp.next.next.getData())>0){
                    swap(tmp,tmp.next);
                }
                tmp=tmp.next;
            }
        }
        return HList;
    }

    //交换节点
    private void swap(ListNode tmp, ListNode next) {
        tmp.next=next.next;
        next.next=tmp.next.next;
        tmp.next.next=next;
    }

    //建树
    public void conTree(HuffmanList HList){
        while (HList.getlen()>1){
            sortedHlist(HList);     //每次都对链表进行排序,从小到大
            HuffmanNode parent=new HuffmanNode();   //创建一个父节点,用来存放两个子节点的权重
            HuffmanNode lnode=HList.getFirstNode(); //获取第一个小,并删除
            HuffmanNode rnode=HList.getFirstNode(); //获取第二个小,并删除
            //把左右孩子赋值给父节点
            parent.setLeftChild(lnode);
            parent.setRightChild(rnode);
            //父节点的权重为两个子节点相加
            parent.setWeight(lnode.getWeight()+rnode.getWeight());
            //把父节点添加会链表
            HList.addNode(parent);
        }
    }

    //前序遍历建立哈夫曼编码
    
    public void setCode(HuffmanNode root,String s){
        //遍历到叶节点,就把哈夫曼编码赋值给叶节点
    	StringBuilder sb=new StringBuilder(); //存放哈夫曼编码
        if (root.getLeftChild()==null&&root.getRightChild()==null){
            root.setHuffmanCode(sb.append(s));
            return;
        }
//            sb.append(0);   //左节点就加0
            setCode(root.getLeftChild(),s+"0");   //左子树遍历
//            sb.deleteCharAt(sb.length()-1); //返回上一层,删除最后一个的编码
//            sb.append(1);   //右节点就加1
            setCode(root.getRightChild(),s+"1");  //右子树遍历
//            sb.deleteCharAt(sb.length()-1); //返回上一层,删除最后一个编码

    }

    //输出树
    public void outNode(HuffmanNode root){
        //输出叶节点的字符,权重跟哈夫曼编码
        if(root.getLeftChild()==null&&root.getRightChild()==null){
            System.out.println(root.getChar()+" "+root.getWeight()+"    "+root.getHuffmanCode());
            return;
        }
        //左子树遍历
        HuffmanNode left=root.getLeftChild();
        if (root.getLeftChild()!=null){
            outNode(left);
        }
        //右子树遍历
        HuffmanNode right=root.getRightChild();
        if (root.getRightChild()!=null){
            outNode(right);
        }
    }

    //编译
    public void decoding(StringBuilder sb, HuffmanNode root, String str){
        char thechar;   //把字符串遍历给thechar
        for (int i=0;i<str.length();i++){
            thechar=str.charAt(i);
            searchChar(sb,root,thechar);  //根据thechar去搜索对应的哈夫曼编码
        }
    }

    //根据字符,搜索哈夫曼编码
    public void searchChar(StringBuilder sb, HuffmanNode root, char thechar){
        if (root==null){
            return;
        }
        //如果相等,就输出,同时,把值赋值给list,方便下一步的反编译查询
        if (root.getChar()==thechar){
            sb.append(root.huffmanCode);
            System.out.print(root.getHuffmanCode());
        }
        searchChar(sb,root.getLeftChild(),thechar);   //递归左子树
        searchChar(sb,root.getRightChild(),thechar);  //递归右子树
    }

    //反编译
    public void endecoding(List<StringBuilder> list, HuffmanNode root){
        StringBuilder sb;   //获取list的数据
        int i=0;    //一个指针
        //当动态列表的长度不为零的时候,就获取
        while (list.size()>0){
            sb=list.remove(i);  //因为动态列表的remove方法,会改变长度的,所以,不用for循环
            searchCode(list,root,sb);   //根据哈夫曼编码搜索char
        }
    }

    //根据哈夫曼编码搜索char
    public void searchCode(List<StringBuilder> list, HuffmanNode root, StringBuilder sb){
        if (root==null){
            return;
        }
        //相等就输出
        if (root.huffmanCode==sb){
            System.out.print(root.getChar());
        }
        searchCode(list,root.getLeftChild(),sb);    //递归左子树
        searchCode(list,root.getRightChild(),sb);   //递归右子树
    }
    
    //反编译
    public void endecoding_1(String str,HuffmanNode root) {
    	int i=0;	//定义一个指针,来判断有没有遍历完字符串
    	HuffmanNode head=root;	//存放根节点的,用来返回
    	StringBuilder sb=new StringBuilder();	//用一个sb来添加字符
    	while(i<str.length()) {
    		char c=str.charAt(i);
    		if(c=='0') {	//左查询
    			root=root.left;
    		}
    		else if(c=='1') {	//右查询
    			root=root.right;
    		}
    		if(root.getLeftChild()==null&&root.getRightChild()==null) {	//遍历到叶节点
    			sb.append(root.getChar());	//获取叶节点的字符
    			root=head;	//重置root
    		}
    		i++;
    	}
    	System.out.print(sb.toString());
    }
    
    public String getContext() {
    	StringBuilder sb=new StringBuilder();
    	String str=new String();
    	File txtFile=new File("D:\\download\\Decoding1\\src\\demo1.txt");
    	try {
    		FileReader read = new FileReader(txtFile);
    		int end;
    		while((end=read.read() )!=-1) {
    			sb.append((char)end);
    		}
    	
    	str=sb.toString();
    	}catch(Exception e) {
    		System.out.print(e);
    	}
		return str;
    }
}


读文本

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * Create on 2021/4/19
* author:chen
 */

//读取文本
public class getContext {
    Map<Character,Float> get() throws FileNotFoundException {
        File txtFile=new File("D:\\download\\Decoding\\src\\Demo.txt");
        Map<Character, Float> txtMap=new HashMap<Character, Float>();   //存放字符跟字符出现的次数
        try{
            FileReader read = new FileReader(txtFile);
            int end;
            //一个一个读取
            while((end=read.read() )!=-1){
                //如果存在一样的字符,就把出现次数+1,然后,只存放a~z,跟, . 跟空格
                if (txtMap.containsKey(((char)end))){
                    float weight=txtMap.get(((char)end));
                    weight+=1;
                    txtMap.put(((char)end),weight);
                }else if (end>='a' && end<='z'){
                    txtMap.put(((char)end),1.0f);
                }else if (end==' '){
                    txtMap.put(((char)end),1.0f);
                }else if (end==','){
                    txtMap.put(((char)end),1.0f);
                }else if (end=='.'){
                    txtMap.put(((char)end),1.0f);
                }
            }
            int len=0;  //计算总的长度
            for (float v:txtMap.values()){
                len+=v;
            }
            //计算权重
            for (Character key:txtMap.keySet()){
                float weight=txtMap.get(key);
                weight=weight/len;
                txtMap.put(key,weight);
            }
//            for (Character key:txtMap.keySet()){
//                System.out.println(key+"    "+txtMap.get(key));
//            }
            read.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return txtMap;
    }
}

main函数

public class Test {
    public static void main(String[] args) throws FileNotFoundException {
        Map<Character,Float> txtMap;
        getContext g=new getContext();
        txtMap=g.get();     //获取文本
        HuffmanTree T=new HuffmanTree();
        HuffmanList List=T.addHlist(txtMap);    //把文本存放进列表
        T.conTree(List);    //建树
//        List.output(List);
        HuffmanNode root=List.getFirstNode();   //根节点
        T.setCode(root,"");    //编码
        T.outNode(root);    //输出root

        Scanner in=new Scanner(System.in);
        //动态链表存放字符对应的哈夫曼编码,然后才可以反编译
        List<StringBuilder> list=new ArrayList<StringBuilder>();
        StringBuilder sb=new StringBuilder();
        System.out.println("please input the txt:");
        String str=T.getContext();
        T.decoding(sb,root,str);  //编译
//        System.out.println();
//        System.out.println("将上述哈夫曼编码编译为:");
//        T.endecoding(list,root);    //反编译
//        str=sb.toString();
//        str=T.getContext();
//        T.endecoding_1(str, root);
    }

}


;