哈夫曼樹的編碼實驗

Java哈夫曼編碼實驗--哈夫曼樹的創建,編碼與解碼

建樹,造樹,編碼,解碼java

1、哈夫曼樹編碼介紹

  • 一、哈夫曼樹:
    • (1)定義:假設有n個權值{w1, w2, ..., wn},試構造一棵含有n個葉子結點的二叉樹,每一個葉子節點帶權威wi,則其中帶權路徑長度WPL最小的二叉樹叫作最優二叉樹或者哈夫曼樹。
    • (2)特色:哈夫曼樹中沒有度爲1的結點,故由n0 = n2+1以及m= n0+n1+n2,n1=0可推出m=2*n0-1,即一棵有n個葉子節點的哈夫曼樹共有2n-1個節點。
  • 二、哈夫曼編碼步驟:
    • (1)計算字符出現的次數:
      • 假設咱們如今有一段文檔須要進行編碼,咱們如今須要對於每個字符出現的次數進行統計,而後分別計算出其在這篇文檔的權重,也就是頻率,咱們用下面的一段文字進行舉例;
      • 在以下的文檔中包括的字符有8個字母和2個空格,因此咱們如今須要去統計這10個字符的個數,統計後的數據以下:
    • (2)對字符出現的次數做爲一個權重,從小到大進行排序;
      • 排序結果:0.1,0.1,0.1,0.1,0.1,0.1,0.2,0.2
    • (3)依照排序結果從小到大根據如下規則進行造樹:
      • a、最小的前兩個數進行權重的相加,造成他們兩個做爲左子樹和右子樹的父結點,若是是左結點就標記爲0,若是是右結點就標記爲1
      • b、而後將父結點做爲一個新的結點進入排序結果,以後進行從新排序(ps:假如出現添加後的父結點的權重和以前排序中的結點的權重相等的狀況,二者的位置具體是對於建樹沒有影響的,可是在編程實現的過程當中,程序須要將二者的位置進行肯定)
      • c、重複上述過程,直到獲得的父結點的權重爲1。
      • d、具體流程以下:
      i like you  //文檔
字符 頻率
i 0.2
l 0.1
k 0.1
e 0.1
y 0.1
o 0.1
u 0.1
空格 0.2

從根向下一次讀取0或者1,進行編碼,編碼結果以下表git

字符 編碼
i 111
l 010
k 011
e 000
y 001
o 100
u 101
空格 110

2、編程實現過程

  • 一、首先我準備了一段英文文檔,包括26個字母和一個空格字符
for many young people they dont have the habit to save money because they think they are young and should enjoy the life quickly so there is no need to save money but saving part of the income can better help us to deal with emergent situations though it is hard to store income index zero we still can figure out some ways
  • 二、而後先進行文件的讀取和進行字符出現的次數統計
//讀取文檔中的英文文檔
        String[] a = new String[800];
        try (FileReader reader = new FileReader("英文文檔");
             BufferedReader br = new BufferedReader(reader)
        ) {
            int b =0;
            for (int i =0;i<800;i++){
                a[b]=br.readLine();
                b++;
            }
            } catch (IOException e) {
            e.printStackTrace();
        }
        String[] b = a[0].split("");
    //    System.out.println(Arrays.toString(b));
//開始構造哈夫曼樹
          Objects Za= new Objects("a",an);
        Objects Zb = new Objects("b",bn);
        Objects Zc = new Objects("c",cn);
        Objects Zd = new Objects("d",dn);
        Objects Ze = new Objects("e",en);
        Objects Zf = new Objects("f",fn);
        Objects Zg = new Objects("g",gn);
        Objects Zh = new Objects("h",hn);
        Objects Zi = new Objects("i",in);
        Objects Zj = new Objects("j",jn);
        Objects Zk = new Objects("k",kn);
        Objects Zl = new Objects("l",ln);
        Objects Zm = new Objects("m",mn);
        Objects Zn = new Objects("n",nn);
        Objects Zo = new Objects("o",on);
        Objects Zp = new Objects("p",pn);
        Objects Zq = new Objects("q",qn);
        Objects Zr = new Objects("r",rn);
        Objects Zs = new Objects("s",sn);
        Objects Zt = new Objects("t",tn);
        Objects Zu = new Objects("u",un);
        Objects Zv = new Objects("v",vn);
        Objects Zw = new Objects("w",wn);
        Objects Zx = new Objects("x",xn);
        Objects Zy = new Objects("y",yn);
        Objects Zz = new Objects("z",zn);
        Objects Zkongge = new Objects(" ",zkongge);

        System.out.println("各個字符的機率統計爲:");
        Objects[] temp = new Objects[]{Za,Zb,Zc,Zd,Ze,Zf,Zg,Zh,Zi,Zj,Zk,Zl,Zm,Zn,Zo,Zp,Zq,Zr,Zs,Zt,Zu,Zv,Zw,Zx,Zy,Zz,Zkongge};
            for (int i =0;i<temp.length;i++){
                System.out.println(temp[i].getName()+"的機率爲"+temp[i].getWeight()/323);
            }
  • 三、由於我用了一個Objects保存了一個元素的名字權重編碼,還有他的左孩子,右孩子
package 哈夫曼樹編碼實驗;

public class Objects implements Comparable<Objects> {
    private String name;
    private double weight;
    private String date;
    private Objects left;
    private Objects right;
    public Objects(String Name , double Weight){
        name=Name;
        weight=Weight;
        date="";
    }
    public String getName() {
        return name;
    }

    public double getWeight() {
        return weight;
    }

    public Objects getLeft() {
        return left;
    }

    public Objects getRight() {
        return right;
    }

    public void setLeft(Objects left) {
        this.left = left;
    }

    public void setRight(Objects right) {
        this.right = right;
    }

    public void setName(String name) {
        this.name = name;
    }

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

    @Override
    public String toString() {
        return "Objects{" + "name='" + name + '\'' + ", weight=" + weight + ", 編碼爲='" + date + '\'' + '}'+"\n";
    }

    @Override
    public int compareTo(Objects o) {
        if (weight>=o.weight){
            return 1;
        }
        else {
            return -1;        //規定發現權重相等向後放;
        }
    }

    public void setDate(String date) {
        this.date = date;
    }

    public String getDate() {
        return date;
    }
}
  • 四、進行哈夫曼樹的創建
List tempp = new ArrayList();
        for (int i =0;i<temp.length;i++){
            tempp.add(temp[i]);
        }
        Collections.sort(tempp);        //將咱們的Objects類中的每個字符放進鏈表進行排序


        while (tempp.size() > 1) {            //直到咱們鏈表只剩下一個元素,也就是咱們的根結點的時候跳出循環
            Collections.sort(tempp);            //排序
            Objects left = (Objects) tempp.get(0);        //獲得第一個元素,做爲左孩子
            left.setDate( "0");    //初始化左孩子的編碼爲0
            Objects right = (Objects) tempp.get(1);    //獲得第二個元素,做爲右孩子
            right.setDate( "1");    //初始化有孩子的編碼爲1
            Objects parent = new Objects(left.getName()+right.getName(), left.getWeight() + right.getWeight());    //構造父結點
            parent.setLeft(left);    //設置左結點
            parent.setRight(right);    //設置右結點
            tempp.remove(left);    //刪除左結點
            tempp.remove(right);    //刪除右結點
            tempp.add(parent);    //將父結點添加進入鏈表
            }
  • 五、開始進行編碼
//開始進行哈夫曼編碼
        Objects root = (Objects) tempp.get(0);    //咱們經過一個root保存爲根結點

        System.out.println( );        //咱們利用先序遍歷,遍歷到每個結點,由於這樣能夠保證都從根結點開始遍歷
        List list = new ArrayList();
        Queue queue = new ArrayDeque();
        queue.offer(root);
        while (!queue.isEmpty()){
            list.add(queue.peek());
            Objects temp1 = (Objects) queue.poll();

            if(temp1.getLeft() != null)
            {
                queue.offer(temp1.getLeft());
                temp1.getLeft().setDate(temp1.getDate()+"0");    //判斷假如爲左結點,就基於結點自己的編碼加上0
                }

            if(temp1.getLeft() != null)
            {
                queue.offer(temp1.getRight());
                temp1.getRight().setDate(temp1.getDate()+"1");    //判斷假如爲右結點,就基於結點自己的編碼加上1
            }
            }
  • 六、開始對於文檔進行加密而且保存進文檔
//進行加密
        String result = "";            //定義了一個字符串,用來保存加密後的文檔
        for (int i =0 ;i<b.length;i++){
            for (int j=0;j<temp.length;j++){
                if (b[i].equals(temp[j].getName())){
                    result+=temp[j].getDate();        //由於如今咱們以前保存Objects的數組中的每個字符已經有各自的編碼,因此咱們用咱們以前保存文檔的數組b進行對於,假如找到相對應的,就將編碼賦給result,進行累加,重複過程
                    break;                                            
                }

            }
        }
        System.out.println("加密後");
        System.out.println(result);
        File file = new File("加密後文檔");
        FileWriter fileWritter = null;
        try {
            fileWritter = new FileWriter(file.getName(),true);
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            fileWritter.write(result);
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            fileWritter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
  • 七、進行解密
//解密,讀取須要解密的文檔
        try (FileReader reader = new FileReader("加密後文檔");
             BufferedReader br = new BufferedReader(reader)
        ) {
            int e =0;
            for (int i =0;i<800;i++){
                a[e]=br.readLine();
                e++;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        String duqu=a[0];    
        String jiemi="";    //保存解密後的文檔
        String temp2="";   // 做爲一個臨時的字符串,一個一個進行獲取密文,當進行匹配成功了之後,變爲空,以下

        for (int i =0;i<duqu.length();i++){
            temp2+=duqu.charAt(i);
            for (int j = 0;j<temp.length;j++){            //這裏解密的思路就是咱們從加密的文檔中一個一個字符進行獲取,而後與咱們的以前建好的Objects數組中的元素的編碼
                if (temp2.equals(temp[j].getDate())){  //進行獲取,而後獲取成功之後將其賦給jiemi,而後清空temp2;
                    jiemi+=temp[j].getName();
                    temp2="";
                }
            }
        }
        System.out.println("解密後");
        System.out.println(jiemi);
        //將解密後的文本寫入文件

        File file1 = new File("解密後文檔");
        FileWriter fileWritter1 = null;
        try {
            fileWritter1 = new FileWriter(file1.getName(),true);
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            fileWritter1.write(jiemi);
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            fileWritter1.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
  • 八、成功完成加密解密,結果以下:

實驗代碼連接

參考資料

java建立哈夫曼樹和實現哈夫曼編碼編程

相關文章
相關標籤/搜索