哈夫曼樹(赫夫曼樹、最優樹)詳解

目錄

哈夫曼樹(赫夫曼樹、最優樹)詳解

哈夫曼樹相關的幾個名詞

什么是哈夫曼樹

構建哈夫曼樹的過程

哈弗曼樹中結點結構

構建哈弗曼樹的算法實現


哈夫曼樹(赫夫曼樹、最優樹)詳解

哈夫曼樹相關的幾個名詞

路徑:在一棵樹中,一個結點到另一個結點之間的通路,稱為路徑。圖 1 中,從根結點到結點 a 之間的通路就是一條路徑。

路徑長度:在一條路徑中,每經過一個結點,路徑長度都要加 1 。例如在一棵樹中,規定根結點所在層數為1層,那么從根結點到第 i 層結點的路徑長度為 i - 1 。圖 1 中從根結點到結點 c 的路徑長度為 3。

結點的權:給每一個結點賦予一個新的數值,被稱為這個結點的權。例如,圖 1 中結點 a 的權為 7,結點 b 的權為 5。

結點的帶權路徑長度:指的是從根結點到該結點之間的路徑長度與該結點的權的乘積。例如,圖 1 中結點 b 的帶權路徑長度為 2 * 5 = 10 。

樹的帶權路徑長度為樹中所有葉子結點的帶權路徑長度之和。通常記作?“WPL”?。例如圖 1 中所示的這顆樹的帶權路徑長度為:

WPL = 7 * 1 + 5 * 2 + 2 * 3 + 4 * 3


圖1 哈夫曼樹

什么是哈夫曼樹

當用 n 個結點(都做葉子結點且都有各自的權值)試圖構建一棵樹時,如果構建的這棵樹的帶權路徑長度最小,稱這棵樹為“最優二叉樹”,有時也叫“赫夫曼樹”或者“哈夫曼樹”。

在構建哈弗曼樹時,要使樹的帶權路徑長度最小,只需要遵循一個原則,那就是:權重越大的結點離樹根越近。在圖 1 中,因為結點 a 的權值最大,所以理應直接作為根結點的孩子結點。

構建哈夫曼樹的過程

對于給定的有各自權值的 n 個結點,構建哈夫曼樹有一個行之有效的辦法:

  1. 在 n 個權值中選出兩個最小的權值,對應的兩個結點組成一個新的二叉樹,且新二叉樹的根結點的權值為左右孩子權值的和;
  2. 在原有的 n 個權值中刪除那兩個最小的權值,同時將新的權值加入到?n–2 個權值的行列中,以此類推;
  3. 重復 1 和 2 ,直到所以的結點構建成了一棵二叉樹為止,這棵樹就是哈夫曼樹。

圖 2 哈夫曼樹的構建過程
?

圖 2 中,(A)給定了四個結點a,b,c,d,權值分別為7,5,2,4;第一步如(B)所示,找出現有權值中最小的兩個,2 和 4 ,相應的結點 c 和 d 構建一個新的二叉樹,樹根的權值為 2 + 4 = 6,同時將原有權值中的 2 和 4 刪掉,將新的權值 6 加入;進入(C),重復之前的步驟。直到(D)中,所有的結點構建成了一個全新的二叉樹,這就是哈夫曼樹。

哈弗曼樹中結點結構

構建哈夫曼樹時,首先需要確定樹中結點的構成。由于哈夫曼樹的構建是從葉子結點開始,不斷地構建新的父結點,直至樹根,所以結點中應包含指向父結點的指針。但是在使用哈夫曼樹時是從樹根開始,根據需求遍歷樹中的結點,因此每個結點需要有指向其左孩子和右孩子的指針。

所以,哈夫曼樹中結點構成用代碼表示為:

  1. //哈夫曼樹結點結構
  2. typedef struct {
  3. int weight;//結點權重
  4. int parent, left, right;//父結點、左孩子、右孩子在數組中的位置下標
  5. }HTNode, *HuffmanTree;

構建哈弗曼樹的算法實現

構建哈夫曼樹時,需要每次根據各個結點的權重值,篩選出其中值最小的兩個結點,然后構建二叉樹。

查找權重值最小的兩個結點的思想是:從樹組起始位置開始,首先找到兩個無父結點的結點(說明還未使用其構建成樹),然后和后續無父結點的結點依次做比較,有兩種情況需要考慮:

  • 如果比兩個結點中較小的那個還小,就保留這個結點,刪除原來較大的結點;
  • 如果介于兩個結點權重值之間,替換原來較大的結點;


實現代碼:

 
  1. //HT數組中存放的哈夫曼樹,end表示HT數組中存放結點的最終位置,s1和s2傳遞的是HT數組中權重值最小的兩個結點在數組中的位置
  2. void Select(HuffmanTree HT, int end, int *s1, int *s2)
  3. {
  4. int min1, min2;
  5. //遍歷數組初始下標為 1
  6. int i = 1;
  7. //找到還沒構建樹的結點
  8. while(HT[i].parent != 0 && i <= end){
  9. i++;
  10. }
  11. min1 = HT[i].weight;
  12. *s1 = i;
  13. i++;
  14. while(HT[i].parent != 0 && i <= end){
  15. i++;
  16. }
  17. //對找到的兩個結點比較大小,min2為大的,min1為小的
  18. if(HT[i].weight < min1){
  19. min2 = min1;
  20. *s2 = *s1;
  21. min1 = HT[i].weight;
  22. *s1 = i;
  23. }else{
  24. min2 = HT[i].weight;
  25. *s2 = i;
  26. }
  27. //兩個結點和后續的所有未構建成樹的結點做比較
  28. for(int j=i+1; j <= end; j++)
  29. {
  30. //如果有父結點,直接跳過,進行下一個
  31. if(HT[j].parent != 0){
  32. continue;
  33. }
  34. //如果比最小的還小,將min2=min1,min1賦值新的結點的下標
  35. if(HT[j].weight < min1){
  36. min2 = min1;
  37. min1 = HT[j].weight;
  38. *s2 = *s1;
  39. *s1 = j;
  40. }
  41. //如果介于兩者之間,min2賦值為新的結點的位置下標
  42. else if(HT[j].weight >= min1 && HT[j].weight < min2){
  43. min2 = HT[j].weight;
  44. *s2 = j;
  45. }
  46. }
  47. }

注意:s1和s2傳入的是實參的地址,所以函數運行完成后,實參中存放的自然就是哈夫曼樹中權重值最小的兩個結點在數組中的位置。

構建哈弗曼樹的代碼實現如下:

 
  1. //HT為地址傳遞的存儲哈夫曼樹的數組,w為存儲結點權重值的數組,n為結點個數
  2. void CreateHuffmanTree(HuffmanTree *HT, int *w, int n)
  3. {
  4. if(n<=1) return; // 如果只有一個編碼就相當于0
  5. int m = 2*n-1; // 哈夫曼樹總節點數,n就是葉子結點
  6. *HT = (HuffmanTree) malloc((m+1) * sizeof(HTNode)); // 0號位置不用
  7. HuffmanTree p = *HT;
  8. // 初始化哈夫曼樹中的所有結點
  9. for(int i = 1; i <= n; i++)
  10. {
  11. (p+i)->weight = *(w+i-1);
  12. (p+i)->parent = 0;
  13. (p+i)->left = 0;
  14. (p+i)->right = 0;
  15. }
  16. //從樹組的下標 n+1 開始初始化哈夫曼樹中除葉子結點外的結點
  17. for(int i = n+1; i <= m; i++)
  18. {
  19. (p+i)->weight = 0;
  20. (p+i)->parent = 0;
  21. (p+i)->left = 0;
  22. (p+i)->right = 0;
  23. }
  24. //構建哈夫曼樹
  25. for(int i = n+1; i <= m; i++)
  26. {
  27. int s1, s2;
  28. Select(*HT, i-1, &s1, &s2);
  29. (*HT)[s1].parent = (*HT)[s2].parent = i;
  30. (*HT)[i].left = s1;
  31. (*HT)[i].right = s2;
  32. (*HT)[i].weight = (*HT)[s1].weight + (*HT)[s2].weight;
  33. }
  34. }

注意,如果使用此程序,對權重值分別為 2、8、7、6、5 的節點構建哈夫曼樹,最終效果如圖 4(A) 所示。但其實,圖 4(B) 中顯示的哈夫曼樹也滿足條件,這兩棵樹的帶權路徑長度相同。
?


圖 4 兩種哈夫曼樹


之所以使用此程序構建的哈夫曼樹,是圖 4(A) 而不是 4(B),是因為在構建哈夫曼樹時,結點 2 和結點 5 構建的新的結點 7 存儲在動態樹組中位置,比權重值為 7 節點的存儲位置還靠后,所以,在程序繼續選擇兩個權值最小的結點時,直接選擇了的葉子結點 6 和 7 。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/39131.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/39131.shtml
英文地址,請注明出處:http://en.pswp.cn/news/39131.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

2023牛客暑期多校訓練營8(A/H/I/J)

目錄 A.Alive Fossils H.Insert 1, Insert 2, Insert 3, ... I.Make It Square J.Permutation and Primes A.Alive Fossils 思路&#xff1a;一開始題意看半天沒看懂&#xff0c;后面發現只需要輸出t組輸入中&#xff0c;都出現過的字符串即可。 代碼&#xff1a; void s…

實驗三 圖像分割與描述

一、實驗目的&#xff1a; &#xff08;1&#xff09;進一步掌握圖像處理工具Matlab&#xff0c;熟悉基于Matlab的圖像處理函數。 &#xff08;2&#xff09;掌握圖像分割方法&#xff0c;熟悉常用圖像描述方法。 二、實驗原理 1.膚色檢測 膚色是人類皮膚重要特征之一&#xff…

7.原 型

7.1原型 【例如】 另外- this指向&#xff1a; 構造函數和原型對象中的this都指向實例化的對象 7.2 constructor屬性 每個原型對象里面都有個constructor屬性( constructor構造函數) 作用&#xff1a;該屬性指向該原型對象的構造函數 使用場景: 如果有多個對象的方法&#…

Springboot 實踐(4)swagger-ui 測試controller

前文項目操作&#xff0c;完成了項目的創建、數據源的配置以及數據庫DAO程序的生成與配置。此文講解利用swagger-ui界面&#xff0c;測試生成的數據庫DAO程序。目前&#xff0c;項目swagger-ui界面如下&#xff1a; 以”用戶管理”為例&#xff0c;簡單講述swagger-ui測試數據庫…

無涯教程-Perl - s函數

描述 這不是功能。這是正則表達式替換運算符。根據PATTERN中指定的正則表達式,將數據替換為REPLACE。與m //一樣,分隔符由s后的第一個字符定義。 語法 以下是此函數的簡單語法- s/PATTERN/REPLACE/返回值 如果失敗,此函數返回0,如果成功,則返回替換次數。 例 以下是顯示…

筆記:移植xenomai到nuc972

xenomai是一個實時操作系統,想要使用它,先要移植I-pipe補丁 補丁在xenomai / ipipe-arm GitLab 我的內核是4.4-248的,合并上去會有幾個小錯誤,隨便改改就好 編譯內核沒有報錯之后,接下來需要修改arch/arm/mach-nuc970/time.c 修改方法參考補丁里面其它設備的定時器驅動,就…

學習Vue:數據綁定的基本概念

在 Vue.js 中&#xff0c;Vue 實例是您構建應用程序的核心。它允許您將數據和界面連接起來&#xff0c;實現動態的數據綁定&#xff0c;使您的應用程序能夠根據數據的變化自動更新界面。讓我們來深入了解 Vue 實例與數據綁定的基本概念。 Vue 實例與數據綁定 什么是 Vue 實例&…

OPC【2】——Relationships

引言 Relationships由一系列Relationship構成。將Abstract Package看做是一個圖數據結構&#xff0c;則Relationships是圖數據中的邊集合。 Package Relationships 對于Package Relationships&#xff0c;其所有引用關系的source對象為Abstract Package&#xff0c;或者看…

【Python機器學習】實驗10 支持向量機

文章目錄 支持向量機實例1 線性可分的支持向量機1.1 數據讀取1.2 準備訓練數據1.3 實例化線性支持向量機1.4 可視化分析 實例2 核支持向量機2.1 讀取數據集2.2 定義高斯核函數2.3 創建非線性的支持向量機2.4 可視化樣本類別 實例3 如何選擇最優的C和gamma3.1 讀取數據3.2 利用數…

Open3D 最小二乘擬合平面(SVD分解法)

目錄 一、算法原理二、代碼實現三、結果展示1、點云2、擬合結果四、優秀博客本文由CSDN點云俠原創,原文鏈接。爬蟲網站自重。 一、算法原理 本文實現矩陣奇異值分解方法的最小二乘擬合平面。原理如下: 對于得到的 n n

歐拉函數(質因子分解)

思路&#xff1a; (1)歐拉函數&#xff1a;輸入n則輸出1~n中與n互質的數的個數。 &#xff08;2&#xff09;計算公式&#xff1a; &#xff08;3&#xff09;證明&#xff1a;&#xff08;容斥原理&#xff09;對于n個數&#xff0c;先分別摘除所有被pi整除的數&#xff0c;…

億信ABI有什么不同,來看最新DEMO演示

為了給用戶營造更好的體驗環境&#xff0c;提供更豐富、更完善的服務&#xff0c;億信華辰旗下核心產品億信ABI DEMO再次上新啦&#xff01;本次億信ABI DEMO環境在原有基礎上煥新升級&#xff0c;帶來了全新的主視覺界面、豐富的行業應用和功能演示DEMO&#xff0c;我們一起來…

季度到季度的組件選擇

組件&#xff1a;<template><div class"quarter"><div class"input-wrap" id"closeId" mouseover"handler" click.stop"btn" :style"{color:colorItem}"><i class"el-icon-date"&…

【Java】BF算法(串模式匹配算法)

?? 什么是BF算法 BF算法&#xff0c;即暴力算法&#xff0c;是普通的模式匹配算法&#xff0c;BF算法的思想就是將目標串S的第一個與模式串T的第一個字符串進行匹配&#xff0c;若相等&#xff0c;則繼續比較S的第二個字符和T的第二個字符&#xff1b;若不相等&#xff0c;則…

【計算機視覺|生成對抗】用深度卷積生成對抗網絡進行無監督表示學習(DCGAN)

本系列博文為深度學習/計算機視覺論文筆記&#xff0c;轉載請注明出處 標題&#xff1a;Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks 鏈接&#xff1a;[1511.06434] Unsupervised Representation Learning with Deep Conv…

騰訊云CVM服務器競價實例是什么?和按量計費有什么區別?

騰訊云服務器CVM計費模式分為包年包月、按量計費和競價實例&#xff0c;什么是競價實例&#xff1f;競價實例和按量付費相類似&#xff0c;優勢是價格更劃算&#xff0c;缺點是云服務器實例有被自動釋放風險&#xff0c;騰訊云服務器網來詳細說下什么是競價實例&#xff1f;以及…

NLP——操作步驟講義與實踐鏈接

數據集與語料 語料是NLP的生命之源&#xff0c;所有NLP問題都是從語料中學到數據分布的規律語料的分類&#xff1a;單語料&#xff0c;平行語料&#xff0c;復雜結構 語料的例子&#xff1a;Penn Treebank, Daily Dialog, WMT-1x翻譯數據集&#xff0c;中文閑聊數據集&#xf…

大數據:Numpy基礎應用詳解

Numpy基礎應用 Numpy 是一個開源的 Python 科學計算庫&#xff0c;用于快速處理任意維度的數組。Numpy 支持常見的數組和矩陣操作&#xff0c;對于同樣的數值計算任務&#xff0c;使用 NumPy 不僅代碼要簡潔的多&#xff0c;而且 NumPy 的性能遠遠優于原生 Python&#xff0c;…

mysql-5.5.62-win32安裝與使用

1.為啥是這個版本而不是當前最新的8.0&#xff1f; 因為我要用32位。目前mysql支持win32的版本最新只到5.7.33。 首先&#xff0c;到官網MySQL :: MySQL Downloads 然后選 選一個自己喜歡的版本就好。我這里是如標題版本。下載32位的zip。然后回來解壓。 完了創建系統環境變…

項目實施方案案例模板-拿來即用

《項目實施方案》實際案例模板&#xff0c;拿來即用&#xff0c;原件可獲取。 項目背景 項目目標 項目范圍 項目總體計劃 項目組織架構 5.1. 項目職責分工 項目風險點 6.1. 項目風險分析 6.2. 項目實施關鍵點 項目管理規范 7.1. 項目實施約束 7.2. 項目變更凍結 7…