第五章 樹和二叉樹(5 7)

2022-09-20 17:57:13 字數 3956 閱讀 9051

目錄5.7.3哈夫曼編碼

2.哈夫曼編碼演算法實現

3.檔案的編碼和解碼

哈夫曼(huffman)樹又稱最優樹,是一類帶權路徑長度最短的樹,在實際中有廣泛的用途。哈夫曼樹的定義,涉及路徑、路徑長度、權等概念,下面先給出這些概念的定義,然後再介紹哈夫曼樹。

路徑:從乙個結點到另乙個結點之間的分支序列

​ 路徑長度:從乙個結點到另乙個結點所經過的分支數目

​ 結點的權:根據應用的需要可以給樹的結點賦權值

​ 帶權路徑長度:從根到該結點的路徑長度與該結點權的乘積

​ 樹的帶權路徑長度:樹中所有葉子結點的帶權路徑之和

​ 哈夫曼樹:由n個帶權葉子結點構成的所有二叉樹中帶權路徑長度最短的二叉樹

!(f:\極客班\資料結構-極客班\第五章 樹和二叉樹\image\20180412213549715.png)

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號單元開始,依次將12n-l所有單元中的雙親、左孩子、右孩子的下標都初始化為o; 最後再迴圈n次,輸入前n個單元中葉子結點的權值。

2.建立樹:迴圈n-1次,通過n1次的選擇、刪除與合併來建立哈夫曼樹。選擇是從當前森林中選擇雙親為0且權值最小的兩個樹根結點sls2;刪除是指將結點sls2的雙親改為非o; 合併就是將sls2的權值和作為乙個新結點的權值依次存入到陣列的第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; 分配臨時儲存每個字元編碼的動

態陣列空間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。

2.2演算法描述
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層。樹的深度 樹中所有結點的最大層數,也稱高度。層序編號 將樹中...