目錄5.7.3哈夫曼編碼
2.哈夫曼編碼演算法實現
3.檔案的編碼和解碼
哈夫曼(huffman)樹又稱最優樹,是一類帶權路徑長度最短的樹,在實際中有廣泛的用途。哈夫曼樹的定義,涉及路徑、路徑長度、權等概念,下面先給出這些概念的定義,然後再介紹哈夫曼樹。
路徑:從乙個結點到另乙個結點之間的分支序列
路徑長度:從乙個結點到另乙個結點所經過的分支數目!(f:\極客班\資料結構-極客班\第五章 樹和二叉樹\image\20180412213549715.png) 結點的權:根據應用的需要可以給樹的結點賦權值
帶權路徑長度:從根到該結點的路徑長度與該結點權的乘積
樹的帶權路徑長度:樹中所有葉子結點的帶權路徑之和
哈夫曼樹:由n個帶權葉子結點構成的所有二叉樹中帶權路徑長度最短的二叉樹
1)初始化:根據給定的n個權值 ,構造n棵只有乙個根結點的二叉樹, n個權值分別是這些二叉樹根結點的權;
2)找最小樹:在f中選取兩棵根結點樹值最小的樹作為左、右子樹,構造一顆新的二叉樹,置新二叉樹根的權值為左、右子樹根結點權值之和;
3)刪除與加入:從f中刪除這兩顆樹,並將新樹加入f;
4)判斷:重複2)和3),直到f中只含一顆樹為止,此時得到的這顆二叉樹就是哈夫曼樹。
哈夫曼樹是一種二叉樹,當然 可以採用前面介紹過的通用儲存方法,而由千哈夫曼樹中沒有度為1的結點,則一棵有n個葉子結點的哈夫曼樹共有2n-i個結點,可以儲存在乙個大小為2n-i的一維陣列中。樹中每個結點還要包含其雙親資訊和孩子結點的資訊,由此,每個結點的儲存結構設計如圖5.27所示。
//- - - - -哈夫曼樹的儲存表示 --- - - .-
typedef struct htnode,*huffmantree; //動態分配陣列儲存哈夫曼樹
哈夫曼樹的各結點儲存在由huffmantree定義的動態分配的陣列中,為了實現方便,陣列的0號單元不使用,從1號單元開始使用,所以陣列的大小為2n。將葉子結點集中儲存在前面部分l~n個位置,而後面的n-1個位置儲存其餘非葉子結點。
3.1演算法步驟
1.初始化:首先動態申請2n個單元;然後迴圈2n-l次,從1號單元開始,依次將1至2n-l所有單元中的雙親、左孩子、右孩子的下標都初始化為o; 最後再迴圈n次,輸入前n個單元中葉子結點的權值。
2.建立樹:迴圈n-1次,通過n—1次的選擇、刪除與合併來建立哈夫曼樹。選擇是從當前森林中選擇雙親為0且權值最小的兩個樹根結點sl和s2;刪除是指將結點sl和s2的雙親改為非o; 合併就是將sl和s2的權值和作為乙個新結點的權值依次存入到陣列的第n+l之後的單元中,同時記錄這個新結點左孩子的下標為sl,右孩子的下標為s2。
3.2演算法描述
void createhuffmantree(huffmantree &ht,int n)
for(i=l;i<=n;++i} //輸人前 n 個單元中葉子結點的權值
cin>>ht[i] .weight;
/*- ---- ----- -初始化工作結束, 下面開始建立哈夫曼樹- - - - ------ */
for (i=n+l; i<=m; ++i}
; //在 ht[k] (l:e;;;k:e;;;i-1) 中選擇兩個其雙親域為0 且權值最小的結點,並返回它們在 ht 中的序號 sl和 s2
ht[sl] .parent=i;ht[s2] .parent=i;
//得到新結點 i, 從森林中刪除sl, s2, 將sl和s2 的雙親域由 0改為l.
ht[i] .lchild=sl;ht [i]. rchild=s2; / /sl, s2分別作為i的左右孩子
ht[i] .weight=ht[sl] .weight+ht[s2] .weight; /八的權值為左右孩子權值之和
}//for
}
1.1概念
(l)字首編碼:如果在乙個編碼方案中,任乙個編碼都不是其他任何編碼的字首(最左子串),則稱編碼是字首編碼。 例 如, 案例5.1中的第2種編碼方案(見表5.1 (b))的編碼 0, 10, 110, 111是字首編碼, 而 第3種編碼方案(見表5.1(c))的編碼 0, 01, 010, 111就不是字首編碼。字首編碼可以保證對壓縮檔案進行解碼時不產生二義性, 確保正確解碼。
(2)哈夫曼編碼:對一棵具有n個葉子的哈夫曼樹,若對樹中的每個左分支 賦予0,右分支賦予1'則從根到每個葉子的路徑上,各分支 的賦值分別構成乙個二進位制串, 該二進位制串就稱為哈夫曼編碼。
1.2性質
性質1 哈夫曼編碼是字首編碼。
性質2 哈夫曼編碼是最優字首編碼。
2.1演算法步驟
1.分配儲存n個字元編碼的編碼表空間hc, 長度為n+l; 分配臨時儲存每個字元編碼的動2.2演算法描述態陣列空間cd, cd[n-1)置為'\o'。
2.逐個求解n個字元的編碼,迴圈 n次,執行以下操作:
• 設定變數start用千記錄編碼在cd中存放的位置,start初始時指向最後,即編碼結束符位置n-1;
• 設定變數c用於記錄從葉子結點向上回溯至根結點所經過的結點下標,c初始時為當前待編碼字元的下標i, f用於記錄i的雙親結點的下標;
• 從葉子結點向上回溯至根結點,求得字元i的編碼,當 f 沒有到達根結點時,迴圈執行以下操作:
► 回溯一次start向前指乙個位置,即- -start;
► 若結點c是 f的左孩子,則生成** 0, 否則生成** 1' 生成的** 0 或 l 儲存在cd[start)中;
► 繼續向上回溯,改變c和f的值。-·
• 根據陣列cd的字串長度為第i個字元編碼分配空間hc[i),然後將陣列cd中的編碼複製到hc[i]中。
3.釋放臨時空間cd。
void creathuffmancode(huffmantree ht,huffmancode &hc,int n)
//求出第l.個字元的編碼
hc[i]=new char[n-start]; //為第i個字元編碼分配空間
strcpy(hc[i],&cd[start]); //將求得的編碼從臨時空間cd複製到hc的當前行中
}delete cd; //釋放臨時空間
}
1.編碼
有了字符集的哈夫曼編碼表之後,對資料檔案的編碼過程是:依次讀入檔案中的字元c, 在哈夫曼編碼表hc中找到此字元,將字元c轉換為編碼表中存放的編碼串
2.解碼
對編碼後的檔案進行解碼的過程必須借助千哈夫曼樹。具體過程是:依次讀入檔案的二進位製碼,從哈夫曼樹的根結點(即ht[m])出發,若當前讀入 0, 則走向左孩子,否則走向右孩子。一旦到達某一葉子ht[i]時便譯出相應的字元編碼hc[i]。然後重新從根出發繼續解碼,直至檔案結束。
第五章樹和二叉樹
樹的邏輯結構 在樹中常常將資料元素稱為結點。任意一棵非空樹滿足以下條件 1 有且僅有乙個特定的稱為根的結點 2 當n 1時,除根結點之外的其餘節點被分成m m 0 個互不相交的有限集合t1,t2.tm,其中每個集合又是一棵樹,並稱為根節點的子樹。樹的定義是遞迴的。結點的度 某結點所擁有的子樹的個數 ...
第五章 樹和二叉樹
5.1 樹的邏輯結構 樹的定義 n n 0 個結點的有限集合。當n 0時,稱為空樹 任意一棵非空樹滿足以下條件 有且僅有乙個特定的稱為根的結點 當n 1時,除根結點之外的其餘結點被分成m m 0 個互不相交的有限集合t1,t2,tm,其中每個集合又是一棵樹,並稱為這個根結點的子樹。樹的基本術語 結點...
第五章樹和二叉樹
結點的度 結點所擁有的子樹的個數。樹的度 樹中各結點度的最大值。葉子結點 度為0的結點,也稱為終端結點。分支結點 度不為0的結點,也稱為非終端結點。結點所在層數 根結點的層數為1 對其餘任何結點,若某結點在第k層,則其孩子結點在第k 1層。樹的深度 樹中所有結點的最大層數,也稱高度。層序編號 將樹中...