1. 程式人生 > >java_實現Haffman樹及其編碼與解碼

java_實現Haffman樹及其編碼與解碼

哈夫曼樹的建立與哈夫曼編碼的實現

目的和要求:

(1)正確定義哈夫曼樹結點

(2)掌握哈夫曼樹的建立方法

(3)掌握根據哈夫曼樹進行編碼的方法

(4)根據哈夫曼編碼解決實際問題

實驗原理及內容:

(1)定義哈夫曼樹結點

(2)哈夫曼樹的建立方法

(3)根據哈夫曼樹進行編碼

實驗步驟:

(1)定義哈夫曼樹結點

(2)哈夫曼樹的建立方法

(3)根據哈夫曼樹進行編碼

實驗過程:

(1)哈夫曼樹結點定義

public class HNode {
	private int weight; //結點權值
    private int lchild; //左孩子結點
    private int rchild; //右孩子結點
    private int parent; //父結點
    private String name;	//結點資料,存放字元名稱
    private String code;//存放葉子結點的字元編碼
    //構造器
    public HNode(String name, int w){
        this.weight = w;
        this.name = name;
        this.lchild = -1;
        this.rchild = -1;
        this.parent = -1;    
        this.code = "";
        
    }
    public HNode(){
        this(null,0);
    }
	public int getWeight() {
		return weight;
	}
	public void setWeight(int weight) {
		this.weight = weight;
	}
	public int getLchild() {
		return lchild;
	}
	public void setLchild(int lchild) {
		this.lchild = lchild;
	}
	public int getRchild() {
		return rchild;
	}
	public void setRchild(int rchild) {
		this.rchild = rchild;
	}
	public int getParent() {
		return parent;
	}
	public void setParent(int parent) {
		this.parent = parent;
	}	
	public String getCode() {
		return code;
	}
	public void setCode(String code) {
		this.code = code;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

(2)哈夫曼樹的定義、建立及哈夫曼編碼、解碼

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class HuffmanTree {
	private HNode[] data; // 結點陣列
	private int leafNum; // 葉子結點數目

	// 構造哈夫曼樹
	public void create() {
		Scanner sc = new Scanner(System.in);
		System.out.println("請輸入要傳輸的報文:");
		String str = sc.nextLine().toLowerCase();
		str = str.replace(" ", "");	//去掉空格
		int[] c = new int[26];
		for (int i = 0; i < str.length(); i++) { // 統計各字元出現的頻率
			c[str.charAt(i) - 'a']++;
		}
		int cnt = 0;
		for (int i = 0; i < 26; i++) { // 統計報文中字元的數量
			if (c[i] > 0)
				cnt++;
		}
		this.leafNum = cnt;

		data = new HNode[this.leafNum * 2 - 1];
		for (int i = 0; i < 2 * leafNum - 1; i++)
			data[i] = new HNode();

		cnt = 0;
		for (int i = 0; i < 26; i++) { // 用字元建立葉子結點
			if (c[i] > 0) {
				data[cnt].setName((char) (i + 'a') + "");
				data[cnt++].setWeight(c[i]);
			}
		}
		int m1, m2, x1, x2;
		// 處理n個葉子結點,建立哈夫曼樹
		for (int i = 0; i < this.leafNum - 1; ++i) {
			m1 = m2 = Integer.MAX_VALUE; // m1:最小權值,m2:次小權值
			x1 = x2 = 0; // x1:權值最小位置,x2:權值次小位置
			// 在全部結點中找權值最小的兩個結點
			for (int j = 0; j < this.leafNum + i; ++j) {
				if ((data[j].getWeight() < m1) && (data[j].getParent() == -1)) {
					m2 = m1;
					x2 = x1;
					m1 = data[j].getWeight();
					x1 = j;
				} else if ((data[j].getWeight() < m2)
						&& (data[j].getParent() == -1)) {
					m2 = data[j].getWeight();
					x2 = j;
				}
			}
			// 用兩個權值最小點構造一個新的中間結點
			data[this.leafNum + i].setWeight(data[x1].getWeight()
					+ data[x2].getWeight());
			data[this.leafNum + i].setLchild(x1);
			data[this.leafNum + i].setRchild(x2);
			// 修改權值最小的兩個結點的父結點指向
			data[x1].setParent(this.leafNum + i);
			data[x2].setParent(this.leafNum + i);
		}
	}
	
	//輸出哈夫曼樹結構
	public void print() {
		System.out.println("位置\t字元\t權值\t父結點\t左孩子結點\t右孩子結點");
		for (int i = 0; i < 2 * leafNum - 1; i++) {
			System.out.printf("%d\t%s\t%d\t%d\t%d\t%d\r\n", i,
					data[i].getName(), data[i].getWeight(),
					data[i].getParent(), data[i].getLchild(),
					data[i].getRchild());
		}
	}

	
	// 前序遍歷,輸出所有葉子結點的編碼,並計算總的報文編碼長度
	private int preorder(HNode root,String code) {		
		int sum = 0;
		if (root != null) {
			root.setCode(code);
			if(isLeaf(root)){		//葉子結點,輸出編碼,並計算長度
				System.out.println(root.getName() + ":" + root.getCode());
				return root.getWeight()*root.getCode().length();
			}
			
			if(root.getLchild()!=-1){
				//左子樹,編碼為0,並統計左子樹葉子結點的編碼長度
				sum +=preorder(data[root.getLchild()],code+"0");
			}
			if(root.getRchild()!=-1){
				//右子樹,編碼為1,並統計右子樹所有葉子結點的編碼長度
				sum +=preorder(data[root.getRchild()],code+"1");
			}			
		}
		return sum;
	}
	
	//層序遍歷,求報文傳輸的總長度
	private void levelOrder(){
		//根結點的位置
		int root = 2*leafNum-2;
		// 根結點為空
		if (root == -1) {
			return;
		}
		// 設定一個佇列儲存層序遍歷的結點
		Queue<HNode> q = new LinkedList<HNode>();
		// 根結點入隊
		q.add(data[root]);		
		int sum = 0;
		String code = "";
		// 佇列非空,結點沒有處理完
		while (!q.isEmpty()) {
			// 結點出隊
			HNode tmp = q.poll();
			code = tmp.getCode();
			// 如果是葉子結點,則計算編碼長度
			if(isLeaf(tmp)){
				sum +=tmp.getWeight()*tmp.getCode().length();
			}
			// 將當前結點的左孩子結點入隊
			if (tmp.getLchild() != -1) {
				q.add(data[tmp.getLchild()]);
				data[tmp.getLchild()].setCode(code+"0");
			}
			if (tmp.getRchild() != -1) {
				// 將當前結點的右孩子結點入隊
				q.add(data[tmp.getRchild()]);
				data[tmp.getRchild()].setCode(code+"1");
			}
		}
		System.out.println("總的報文長度為:"+sum);
	}
	//採用層序遍歷,進行報文解碼
	public String decodes(String codes){
		//根結點的位置
		int root = 2*leafNum-2;
		// 根結點為空
		if (root == -1) {
			return "";
		}
		// 設定一個佇列儲存層序遍歷的結點
		Queue<HNode> q = new LinkedList<HNode>();
		// 根結點入隊
		q.add(data[root]);	
		int i = 0;
		String str = "";
		// 佇列非空,結點沒有處理完
		while (!q.isEmpty()) {
			// 結點出隊
			HNode tmp = q.poll();
			if(!codes.startsWith(tmp.getCode())) continue;
			// 如果是葉子結點,則計算編碼長度
			if(isLeaf(tmp)){
				str = str + tmp.getName();
				codes = codes.substring(tmp.getCode().length());
				if(codes.length()>0){ //如果存在多個報文字元,則繼續重新解碼
					while(!q.isEmpty()) q.poll();
					q.add(data[root]);	
					continue;
				}
			}
			// 將當前結點的左孩子結點入隊
			if (tmp.getLchild() != -1) {
				q.add(data[tmp.getLchild()]);				
			}
			if (tmp.getRchild() != -1) {
				// 將當前結點的右孩子結點入隊
				q.add(data[tmp.getRchild()]);				
			}
		}
		return str;
	}
	// 層次遍歷
	public void traverse() {
		//根結點的位置
		int root = 2*leafNum-2;
		// 根結點為空
		if (root == -1) {
			return;
		}
		int sum = preorder(data[root],"");	
		System.out.println("所有報文長度為(位):"+sum);
	}

	// 判斷是否是葉子結點
	public boolean isLeaf(HNode p) {
		return ((p != null) && (p.getLchild() == -1) && (p.getRchild() == -1));
	}
	
}

(3)哈夫曼樹測試

import java.util.Scanner;
public class TestHuffmanTree {
	// 測試哈夫曼樹
		public static void main(String[] args) {
			HuffmanTree ht = new HuffmanTree();
			ht.create();	//建立哈夫曼樹
			//ht.print();		//輸出哈夫曼樹結構
			ht.traverse();	//輸出所有字元編碼
			String op = "";
			do{
				System.out.println("請輸入一個報文編碼進行解碼:");
				Scanner sc = new Scanner(System.in);
				String codes = sc.nextLine();
				String decodes = ht.decodes(codes);	//報文解碼
				if(decodes.length()==0){
					System.out.println("解碼出錯!");
				}else{
					System.out.println("對應的報文為:"+decodes);
				}
				System.out.println("按X鍵退出,其他鍵繼續");
				op = sc.nextLine();
			}while(!op.toLowerCase().equals("x"));
			System.out.println("程式退出");
		}
}

相關推薦

java_實現Haffman及其編碼解碼

哈夫曼樹的建立與哈夫曼編碼的實現 目的和要求: (1)正確定義哈夫曼樹結點 (2)掌握哈夫曼樹的建立方法 (3)掌握根據哈夫曼樹進行編碼的方法 (4)根據哈夫曼編碼解決實際問題 實驗原理及內容: (1)定義哈夫曼樹結點 (2)哈夫曼樹的建立方法 (3)根據哈

哈夫曼編碼解碼

#include<stdio.h> #include<stdlib.h> #include<iostream> #include<string> using namespace std; #define MAXSIZE 30

DS二叉——Huffman編碼解碼

題目描述 1、問題描述 給定n個字元及其對應的權值,構造Huffman樹,並進行huffman編碼和譯(解)碼。 構造Huffman樹時,要求左子樹根的權值小於、等於右子樹根的權值。 進行Huffman編碼時,假定Huffman樹的左分支上編碼為‘0’,右

20172303 2018-2019-1《程式設計資料結構》哈夫曼編碼解碼

20172303 2018-2019-1《程式設計與資料結構》哈夫曼樹編碼與解碼 哈夫曼樹簡介 定義:給定n個權值作為n個葉子結點,構造一棵二叉樹,若帶權路徑長度達到最小,稱這樣的二叉樹為最優二叉樹,也稱為哈夫曼樹(Huffman Tree)。哈夫曼樹是帶權路徑長度最短的樹,權值較大的結點離根較近。

20172303 2018-2019-1《程序設計數據結構》哈夫曼編碼解碼

exce eat temp 基礎 第一個 最小 charat 轉換 except 20172303 2018-2019-1《程序設計與數據結構》哈夫曼樹編碼與解碼 哈夫曼樹簡介 定義:給定n個權值作為n個葉子結點,構造一棵二叉樹,若帶權路徑長度達到最小,稱這樣的二叉樹為最

Huffman(哈夫曼)編碼解碼程式(全)

關於Huffman樹構建與編碼的原理,很多書上有介紹,我在這裡就只給出相應的程式,包括樹的構建,2種編碼方法,譯碼(這部分是我自己獨立寫的,肯定有不當之處,歡迎回帖指正)等,裡面註釋也很清晰,費了很大勁,希望對大家有幫助。 <span style="font-siz

用python實現base64編碼解碼

用到了python裡的base64模組 用法: 編碼: 1 import base64 2 a = 'HC'.decode() #將‘HC’轉為二進位制 3 b = base64.b64encode(a) #將a轉為base64編碼 4 b.decode() #從二進位制轉回 5 6 base6

【APACHE MINA2.0開發之二】自定義實現SERVER/CLIENT端的編解碼工廠(自定義編碼解碼器)!

在上一篇博文中已經簡單介紹過“過濾器”的概念,那麼在Mina 中的協議編解碼器通過過濾器 ProtocolCodecFilter 構造,這個過濾器的構造方法需 要一個 ProtocolCodecFactory,這從前面註冊 TextLineCodecFactory 的程式碼就可以看出來。 Protoc

.net C#實現Base64編碼解碼

一、編碼規則       Base64編碼的思想是是採用64個基本的ASCII碼字元對資料進行重新編碼。它將需要編碼的資料拆分成位元組陣列。以3個位元組為一組。按順序排列24 位資料,再把這24位資料分成4組,即每組6位。再在每組的的最高位前補兩個0湊足一個位元組。這樣就把一

C#實現Base64編碼解碼 自定義

/// <summary> /// Base64編碼類。 /// 將byte[]型別轉換成Base64編碼的string型別。 ///</summary> public class Base64Encoder { byte[] source; int length, length2;

編碼解碼

文本 也有 文件的 一位 pri 一行 word 終端 二進制位 編碼:真實字符與二進制串的對應關系,真實字符→二進制串 解碼:二進制串與真實字符的對應關系,二進制串→真實字符 首先,明確一點,計算機中存儲的信息都是二進制的 編碼/解碼本質上是一種映射(對應關系),比如

從Python的角度來看編碼解碼

異常 字符 default 疑問 習慣 中文字符集 nbsp prompt ans 導語: Python2和Python3中,因為默認字符集的不同而造成的麻煩,簡直是程序員的夢魘!要徹底告別這個麻煩,就需要從本質上來理解編碼和解碼。 為什麽要有編碼? 對於不會英文的中國

Java-IO流之轉換流的使用和編碼解碼原理

鍵盤輸入 tostring delet 特點 rgb utf8 equals pri 數據 一、理論: 1、字符流和字節流區別是什麽? 字符流=字節流+編碼集,在實際讀取的時候其實字符流還是按照字節來讀取,但是會更具編碼集進行查找編碼集字典解析相應的字節,使得一次讀取出一個

html 編碼解碼

fine 動態創建 innertext 編碼 document 元素 tco innerhtml content var HtmlUtil = { /*1.用瀏覽器內部轉換器實現html轉碼*/ htmlEncode:function (html){

JavaScript進行UTF-8編碼解碼

str 前端 轉載 clas utf-8 處理 序列 一個 ket JavaScript本身可通過charCodeAt方法得到一個字符的Unicode編碼,並通過fromCharCode方法將Unicode編碼轉換成對應字符。 但charCodeAt方法得到的應該是一個16

Python3.X Socket 一個編碼解碼的坑

time() 解碼 oss 數據 tex href real byte 錯誤 最近在看《Python核心編程》第三版 講述網絡編程Socket的知識,在練習中采用Python 3 的代碼中遇到一個與編碼解碼有關的坑,本文將給予詳細的介紹。 軟件環境 Python: 3.6.

08_Python編碼解碼

文字 新的 方式 服務 nbsp 存儲器 san har hang 一、編碼的由來 因為計算機只能處理010101二進制數據,如果要處理文本,圖像,視頻等,需要我們把數據轉換成01010二進制格式才能被計算機處理 最先出現的是ASCII,用8位一個字節來表示,成為單字節碼,

python字符串格式和編碼解碼問題

連接 hello 列表 enc nbsp utf 而不是 取數據 無符號 1 %c 轉換成字符(ASCII碼值,長度為一的字符串) 2 3 %r 有線使用repr()函數進行字符串轉換 4 5 %s 有線使用str()函數進行字符串轉換 6

Python3中字符串的編碼解碼以及編碼之間轉換(decode、encode)

python3 encode 由於 表示 nic code .... 以及 mage 一、編碼 二、編碼與解碼 Python3中對py文件的默認編碼是urf-8。但是字符串的編碼是Unicode。 由於Unicode采用32位4個字節來表示一個字符,存儲和傳輸太浪費資

【轉】python基礎-編碼解碼

什麽 浪費 2.x sys 拼接 aced tro lte bytes 【轉自:https://www.cnblogs.com/OldJack/p/6658779.html】 一、什麽是編碼 編碼是指信息從一種形式或格式轉換為另一種形式或格式的過程。 在計算機中,編碼,簡而