[英]Storing and reconstruction of Huffman tree
使霍夫曼樹脫水的最佳方法是什么,通過脫水我指的是給霍夫曼樹,以及每片葉子中的字符,如何才能有效地存儲此樹的結構並隨后對其進行重構。
采取下面的樹:
---------------garbage------
-------------/-------\------
------------A-------garbage-
--------------------/-----\-
-------------------B-------C-
一個想法可能是在每個級別上存儲符號,然后使用此信息來重構樹。 在這種情況下:A1B2C2。 因此,我如何首先獲得關卡,並將每個關卡與角色相關聯。
您幾乎可以肯定不需要存儲樹本身。 您可以這樣做,並且不應占用您認為需要的空間,但是通常沒有必要。
如果霍夫曼碼是規范的,則只需存儲每個符號的位長,因為這是生成規范編碼所需的全部信息。 每個符號的位數相對較少,因此應該相當緊湊。 您還可以進一步壓縮該信息(請參閱Aki Suihkonen的回答 )。
自然,代碼的位長與樹的深度基本相同,所以我認為這大致就是您要問的。 重要的部分是在給定長度的情況下,知道如何構建規范代碼-它不一定與遍歷樹所產生的代碼相同。 你可以從這個再生一棵樹,但它不一定是你開始與樹-但是通常你不需要其他的樹,而不是確定在首位的代碼長度。
生成規范代碼的算法非常簡單:
取字符串“香蕉”。 顯然,使用了3個符號“ b”,“ a”和“ n”,其計數分別為1、3和2。
所以樹可能看起來像這樣:
* / \ * a / \ b n
天真的,這可以給出代碼:
a = 1 b = 00 n = 01
但是,如果相反,您僅使用位長作為規范代碼生成的輸入,則會產生以下結果:
a = 0 b = 10 n = 11
它是不同的代碼,但顯然它將產生相同長度的壓縮輸出。 此外,您只需要存儲代碼長度即可重現代碼。
因此,您只需要存儲一個序列:
0... 1 2 0... 2 0...
其中“ ...”表示易於壓縮的重復,並且所有值都將非常小(每個值可能只有4位-並請注意,符號根本沒有存儲)。 這種表示將非常緊湊。
如果您確實必須存儲樹本身,則一種技術是遍歷樹並存儲單個位以指示節點是內部節點還是葉節點,然后為葉節點存儲符號代碼。 對於不包含每個符號的樹來說,這是相當緊湊的,即使對於相當完整的樹,也不太差。 最壞的情況是所有符號的總大小,再加上節點數。 對於標准的8位字節流,這將是320個字節(代碼為256個字節,樹結構本身為511個位)。
該方法是從根節點開始,並針對每個節點:
要進行重構,請執行類似的遞歸過程,但顯然需要讀取數據並選擇是否遞歸創建子項或適當地讀取符號。
對於上面的示例,樹的位流將類似於:
0, 0, 1, 'b', 1, 'n', 1, 'a'
樹的5位,再加上符號的3個字節,四舍五入到4個字節的存儲空間。 但是,當您添加更多符號時,它將迅速增長,而存儲代碼長度則不會。
zlib規范解釋說,存儲霍夫曼樹只需要每個符號的位長。 例如,如果有人為A = 101,B = 111,C = 110,D = 01構造一棵樹,則只需簡單地計算位長並從該長度中重新生成樹,這樣關鍵字將是連續的-> A = 101, B = 110,C = 111,D = 01。 (或以下代碼產生的結果)
設置bl_count[2]=1, bl_count[3]=3
並迭代:
code = 0; // From z-lib specification, RFC 1951
bl_count[0] = 0;
for (bits = 1; bits <= MAX_BITS; bits++) {
code = (code + bl_count[bits-1]) << 1;
next_code[bits] = code;
}
由於最大符號長度將小於16,因此每個符號最多需要4位來存儲這些長度:3,3,3,2 == 0011 0011 0011 0010; 但是,zlib / deflate效果更好-它的運行長度使用轉義符號對這些符號進行編碼,例如16 == 3的游程,17:4的游程等,以進一步壓縮符號長度流。 此外,RLE的長度為零,即缺少字符。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.