贪婪算法在解决哈夫曼树及编码问题中的应用
哈夫曼編碼,是一種可變字長(zhǎng)編碼(VLC)的高效算法。該算法是Huffman于1952年提出一種編碼方法,該方法完全依據(jù)字符出現(xiàn)概率來(lái)構(gòu)造異字頭的平均長(zhǎng)度最短的碼字,有時(shí)稱(chēng)之為最佳編碼。
相比定長(zhǎng)編碼來(lái)說(shuō),這種編碼實(shí)現(xiàn)的壓縮率(衡量壓縮算法效率的重要指標(biāo))非常高,也就是說(shuō),哈夫曼編碼比定長(zhǎng)編碼占用更少的存儲(chǔ)空間。
假設(shè)我們要對(duì)某個(gè)字母表創(chuàng)建一套二進(jìn)制前綴碼,那么我們一般都會(huì)講字母表中的字符與二進(jìn)制的葉子聯(lián)系起來(lái),樹(shù)中所有的左向邊都為0,右向邊都為1.可以通過(guò)記錄根到字符葉子的簡(jiǎn)單路徑上的標(biāo)記來(lái)獲得一個(gè)字符的代碼字。這樣任何一棵這樣的樹(shù)都可以生成一套前綴碼。但是我們都知道即使在英文單詞中,每個(gè)字母出現(xiàn)的概率都是不同的,如果僅僅是放到二叉樹(shù)中,對(duì)于一些高頻字符很可能需要更長(zhǎng)的代碼串來(lái)表示,這是非常不友好的。
一個(gè)方法就是通過(guò)根據(jù)字符出現(xiàn)的概率,盡可能將短位串分配給高頻字符,長(zhǎng)位串分配給低頻字符。這里就用到了貪婪思想。
思路:
1. 初始化n個(gè)單節(jié)點(diǎn)的數(shù),表上字母表中的字符,并將其概率記錄,用來(lái)表示權(quán)重。
2. 找出兩顆權(quán)重最小的樹(shù)(對(duì)于權(quán)重都相同的樹(shù),任選其一),將它們作為新樹(shù)中的左右子樹(shù),并將權(quán)值之和記錄到新的樹(shù)根中。迭代這一步操作,直到剩下一棵單獨(dú)的樹(shù)。
以下面的例子來(lái)描述哈夫曼樹(shù)的構(gòu)造過(guò)程:
| 字符 | A | B | C | D | _ |
| 出現(xiàn)概率 | 0.35 | 0.1 | 0.2 | 0.2 | 0.15 |
由上面的過(guò)程我們得到了下面的代碼字:
| 字符 | A | B | C | D | _ |
| 出現(xiàn)概率 | 0.35 | 0.1 | 0.2 | 0.2 | 0.15 |
| 代碼字 | 11 | 100 | 00 | 01 | 101 |
Input:
5
A B C D _
35 10 20 20 15
Output:
A : 11
B : 100
C : 00
D : 01
_ : 101
完整代碼如下:
import java.util.Scanner; public class Main {//建立數(shù)的節(jié)點(diǎn)類(lèi)static class Node{int weight;//頻數(shù)int parent;int leftChild;int rightChild;public Node(int weight, int parent, int leftChild, int rightChild){this.weight = weight;this.parent = parent;this.leftChild = leftChild;this.rightChild = rightChild;}void setWeight(int weight){this.weight = weight;}void setParent(int parent){this.parent = parent;}void setLeftChild(int leftChild){this.leftChild = leftChild;}void setRightChild(int rightChild){this.rightChild = rightChild;}int getWeight(){return weight;}int getParent(){return parent;}int getLeftChild(){return leftChild;}int getRightChild(){return rightChild;}}//新建哈夫曼編碼static class NodeCode {String character;String code;NodeCode(String character, String code) {this.character = character;this.code = code;}NodeCode(String code) {this.code = code;}void setCharacter(String character) {this.character = character;}void setCode(String code) {this.code = code;}String getCharacter() {return character;}String getCode() {return code;}}//初始化一個(gè)哈弗曼樹(shù)public static void initHuffmanTree(Node[] huffmanTree, int m){for(int i = 0; i < m; i++){huffmanTree[i] = new Node(0, -1, -1, -1);}}//初始化編碼public static void initHuffmanCode(NodeCode[] huffmanCode, int n){for(int i = 0; i < n; i++){huffmanCode[i] = new NodeCode("","");}}//獲取huffmanCode的符號(hào)public static void getHuffmanCode(NodeCode[] huffmanCode, int n){Scanner input = new Scanner(System.in);for(int i = 0; i < n; i++){String temp = input.next();huffmanCode[i] = new NodeCode(temp,"");}}//獲取頻率public static void getHuffmanWeight(Node[] huffmanTree , int n){Scanner input = new Scanner(System.in);for(int i = 0; i < n;i ++){int temp = input.nextInt();huffmanTree[i] = new Node(temp, -1, -1, -1);}}//選取兩個(gè)較小的結(jié)點(diǎn)public static int[] selectMin(Node[] huffmanTree ,int n) {int min[] = new int[2];class TempNode {int newWeight;//存儲(chǔ)權(quán)int place;//存儲(chǔ)該結(jié)點(diǎn)所在的位置TempNode(int newWeight, int place){this.newWeight = newWeight;this.place = place;}void setNewWeight(int newWeight){this.newWeight = newWeight;}void setPlace(int place){this.place = place;}int getNewWeight(){return newWeight;}int getPlace(){return place;}}TempNode[] tempTree = new TempNode[n];//將huffmanTree中沒(méi)有雙親的結(jié)點(diǎn)存儲(chǔ)到tempTree中int i=0,j=0;for(i = 0; i < n; i++) {if(huffmanTree[i].getParent() == -1 && huffmanTree[i].getWeight()!=0) {tempTree[j] = new TempNode(huffmanTree[i].getWeight(),i);j++;}}int m1,m2;m1 = m2 = 0;for(i = 0; i < j; i++) {if(tempTree[i].getNewWeight() < tempTree[m1].getNewWeight())//此處不讓取到相等,是因?yàn)榻Y(jié)點(diǎn)中有相同權(quán)值的時(shí)候,m1取最前的m1 = i;}for(i = 0; i < j; i++) {if(m1 == m2)m2++;//當(dāng)m1在第一個(gè)位置的時(shí)候,m2向后移一位if(tempTree[i].getNewWeight() <= tempTree[m2].getNewWeight() && i != m1)//此處取到相等,是讓在結(jié)點(diǎn)中有相同的權(quán)值的時(shí)候,//m2取最后的那個(gè)。m2 = i;}min[0] = tempTree[m1].getPlace();min[1] = tempTree[m2].getPlace();return min;}//創(chuàng)建哈弗曼樹(shù)public static void createHaffmanTree(Node[] huffmanTree,int n){if(n <= 1)System.out.println("Parameter Error!");int m = 2 * n - 1;//initHuffmanTree(huffmanTree,m);for(int i = n; i < m; i++) {int[] min = selectMin(huffmanTree, i);int min1 = min[0];int min2 = min[1];huffmanTree[min1].setParent(i);huffmanTree[min2].setParent(i);huffmanTree[i].setLeftChild(min1);huffmanTree[i].setRightChild(min2);huffmanTree[i].setWeight(huffmanTree[min1].getWeight() + huffmanTree[min2].getWeight());}}//創(chuàng)建哈夫曼編碼public static void createHaffmanCode(Node[] huffmanTree,NodeCode[] huffmanCode,int n){Scanner input = new Scanner(System.in);char[] code = new char[10];int start;int c;int parent;int temp;code[n-1] = '0';for(int i = 0; i < n; i++){StringBuffer stringBuffer = new StringBuffer();start = n-1;c = i;while((parent=huffmanTree[c].getParent()) >= 0){start--;code[start] = ((huffmanTree[parent].getLeftChild() == c) ? '0' : '1');c = parent;}for(;start < n-1; start++){stringBuffer.append(code[start]);}huffmanCode[i].setCode(stringBuffer.toString());}}//輸出public static void ouputHaffmanCode(NodeCode[] huffmanCode,int n){for(int i = 0; i < n; i++){System.out.println(huffmanCode[i].getCharacter() + " : " + huffmanCode[i].getCode());}}//主函數(shù)public static void main(String[] args){Scanner input = new Scanner(System.in);int n;int m;n = input.nextInt();m = 2*n-1;Node[] huffmanTree = new Node[m];NodeCode[] huffmanCode = new NodeCode[n];//初始化initHuffmanTree(huffmanTree, m);initHuffmanCode(huffmanCode, n);//獲取符號(hào)getHuffmanCode(huffmanCode, n);//獲取概率getHuffmanWeight(huffmanTree, n);//創(chuàng)建哈夫曼樹(shù)createHaffmanTree(huffmanTree, n);//創(chuàng)建哈夫曼編碼createHaffmanCode(huffmanTree, huffmanCode, n);//輸出ouputHaffmanCode(huffmanCode, n);} }注意:輸出哈夫曼樹(shù)和輸出哈夫曼編碼時(shí)不同的操作。總結(jié)
以上是生活随笔為你收集整理的贪婪算法在解决哈夫曼树及编码问题中的应用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: c++教程下载
- 下一篇: 自定义枚举typeHandler