1. 程式人生 > >哈弗曼樹與哈夫曼編碼

哈弗曼樹與哈夫曼編碼

目錄

  • 一、什麼是哈夫曼樹(Huffman Tree)
    • 1.1 哈夫曼樹的定義
  • 二、哈夫曼樹的構造
    • 2.1 哈夫曼樹的特點
  • 三、哈夫曼編碼
    • 3.1 使用二叉樹編碼
    • 3.2 使用哈夫曼樹編碼

更新、更全的《資料結構與演算法》的更新網站,更有python、go、人工智慧教學等著你:https://www.cnblogs.com/nickchen121/p/11407287.html

一、什麼是哈夫曼樹(Huffman Tree)

如果我們將百分制的考試成績轉換成五分制的成績,我們可以使用如下所示的程式:

/* c語言實現 */

if( score < 60 )  grade =1;
else if( score < 70 ) grade =2; 
else if( score < 80 ) grade =3; 
else if( score < 90 ) grade =4;
else grade =5;

通過上述的程式碼,我們可以構造出如下圖所示的判定樹:

如果在上述五分制的成績中,我們考慮學生成績的分佈的概率,如下圖所示:

通過學生成績分佈的概率和上述的判定樹,我們可以得到學生成績的查詢效率為:

\[ 0.05× 1+0.15 ×2+0.4× 3+0.3 ×4+0.1× 4 = 3.15 \]
從學生成績分佈的概率中,可以看出70-79和80-89分佈中的學生較多,然而他們的查詢效率確是較低的,因此我們可以按照如下方式修改程式碼和判定樹:

/* c語言實現 */

if( score < 80 )   
{      
  if( score < 70 );
   if( score < 60 ) {
     grade =1; 
   } else grade = 2; 
  else grad=3; 
}
else if( score < 90 ) grade =4; 
else grade =5; 

通過此次修改,學生成績的查詢效率為:
\[ 0.05× 3+0.15 ×3+0.4× 2+0.3 ×2+0.1× 2 = 2.2 \]
通過上述的例子,我們可以思考一個問題:如何根據結點不同的查詢頻率構造更有效的搜尋樹?

1.1 哈夫曼樹的定義

帶權路徑長度(WPL):設二叉樹有n個葉子結點,每個葉子結點帶有權值\(w_k\),從根節點到每個葉子結點的長度為\(l_k\),則每個葉子結點的帶權路徑長度之和就是:\(WPL = \sum_{k=1}^nw_kl_k\)

最優二叉樹或哈夫曼樹:WPL最小的二叉樹

例:有五個葉子結點,它們的權值為 {1, 2, 3, 4, 5} ,用此權值序列可以構造出形狀不同的多個二叉樹。

二、哈夫曼樹的構造

每次把權值最小的兩顆二叉樹合併

/* c語言實現 */

typedef struct TreeNode *HuffmanTree;
struct TreeNode{
  int Weight;
  HuffmanTree Left, Right;
}

HuffmanTree Huffman( MinHeap H )
{
  // 假設H->Size個權值已經存在H->Elements[]->Weight裡
  int i; HuffmanTree T;
  BuildMinHeap(H); // 將H->Elements[]按權值調整為最小堆
  for (i = 1; i < H->Size; i++)
  {
    // 做H->Size-1次合併
    T = malloc(sizeof(struct TreeNode)); // 建立新結點
    T->Left = DeleteMin(H); // 從最小堆中刪除一個結點,作為新T的左子結點
    T->Right = DeleteMin(H); // 從最小堆中刪除一個結點,作為新T的右子結點
    T->Weight = T->Left->Weight+T->Right->Weight; // 計算新權值
    Insert(H, T); // 將新T插入最小堆
  }
  T = DeleteMin(H);
  return T;
}   
# python語言實現

# 節點類
class Node(object):
    def __init__(self, name=None, value=None):
        self._name = name
        self._value = value
        self._left = None
        self._right = None


# 哈夫曼樹類
class HuffmanTree(object):

    # 根據Huffman樹的思想:以葉子節點為基礎,反向建立Huffman樹
    def __init__(self, char_weights):
        self.a = [Node(part[0], part[1]) for part in char_weights]  # 根據輸入的字元及其頻數生成葉子節點
        while len(self.a) != 1:
            self.a.sort(key=lambda node: node._value, reverse=True)
            c = Node(value=(self.a[-1]._value + self.a[-2]._value))
            c._left = self.a.pop(-1)
            c._right = self.a.pop(-1)
            self.a.append(c)
        self.root = self.a[0]
        self.b = list(range(10))  # self.b用於儲存每個葉子節點的Haffuman編碼,range的值只需要不小於樹的深度就行

    # 用遞迴的思想生成編碼
    def pre(self, tree, length):
        node = tree
        if (not node):
            return
        elif node._name:
            print(node._name + '的編碼為:')
            for i in range(length):
                print(self.b[i])
            print()
            return
        self.b[length] = 0
        self.pre(node._left, length + 1)
        self.b[length] = 1
        self.pre(node._right, length + 1)

    # 生成哈夫曼編碼   
    def get_code(self):
        self.pre(self.root, 0)


if __name__ == '__main__':
    # 輸入的是字元及其頻數
    char_weights = [('a', 5), ('b', 4), ('c', 10), ('d', 8), ('f', 15), ('g', 2)]
    tree = HuffmanTree(char_weights)
    tree.get_code()

上述過程的時間複雜度為:O(N logN)

2.1 哈夫曼樹的特點

  • 沒有度為1的結點;
  • n個葉子結點的哈夫曼樹共有2n-1個結點

  • 哈夫曼樹的任意非葉結點的左右子樹交換後仍是哈夫曼樹;
  • 對同一組權值 \({W_1, W_2, \cdots, W_n}\),是否存在不同構的兩顆哈夫曼樹呢?
    • 對一組權值 {1, 2, 3, 3},可以有如下圖所示的不同構的兩顆哈夫曼樹:

三、哈夫曼編碼

給定一段字串,如何對字元進行編碼,可以使得該字串的編碼儲存空間最少?

例:假設有一段文字,包含58個字元,並由以下7個字元構成:a,e,i,s,t,空格(sp),換行(nl)。這7個字元出現的次數不同。如何對這7個字元進行編碼,使得總編碼空間最少?

分析:

  1. 用等長ASCCII編碼:58*8 = 464位;
  2. 用等長3位編碼:58*3 = 174位;
  3. 不等長編碼:出現頻率高的字元用的編碼短些,出現頻率低的字元可以編碼長些?

對於上述問題,我們如果使用下圖所示方式進行不等長編碼:

很明顯,可以發現上圖所示的不等長編碼具有二義性,因此我們可以使用字首碼的方式解決二義性問題。

字首碼(prefix code):任何字元的編碼都不是另一字元編碼的字首。

3.1 使用二叉樹編碼

使用二叉樹編碼,我們需要注意以下兩個問題:

  1. 左右分支:0、1
  2. 字元只在葉結點上

假設四個字元的頻率為:a:4,u:1,x:2,z:1;那麼我們可以使用最普通的二叉樹對這四個字元進行編碼,如下圖所示:

通過上圖可以發現,我們使用最偷懶的方式,把四個字元放在上述二叉樹的四個葉子結點上,因此我們可以考慮使用如下所示的方法,降低二叉樹的編碼代價:

綜上:哈夫曼編碼需要解決的一個問題為——如何構造一顆編碼代價最小的二叉樹。

3.2 使用哈夫曼樹編碼

對於我們提出來的7個字元的例子,如果我們得知這7個字元的分佈概率為如下圖所示:

我們可以使用構造哈夫曼樹的方式,進行哈夫曼編碼,編碼流程如下:

最終結果如下圖所示:

相關推薦

編碼

目錄 一、什麼是哈夫曼樹(Huffman Tree) 1.1 哈夫曼樹的定義 二、哈夫曼樹的構造 2.1 哈夫曼樹的特點 三、哈夫曼編碼 3

5.2——編碼

node i++ insert 編碼 urn all IV right style #include <stdio.h> #include <stdlib.h> struct TreeNode{ int Weight; Huffm

資料結構——第二章和森林:04編碼

1.結點的路徑長度:從根結點到該結點的路徑上分支的數目。 2.樹的路徑長度:樹中每個結點的路徑長度之和。 3.樹的帶權路徑長度:樹中所有葉子結點的帶權路徑長度之和WPL(T) = ∑wklk(對所有葉子結點) 4.最優樹:在所有含n個結點,並帶相同權值的m叉樹中,必存在一棵其帶權路徑長度取最小值的樹,稱

數據結構——第二章和森林:04編碼

一個 例如 stat state 森林 ont 技術 圖片 http 1.結點的路徑長度:從根結點到該結點的路徑上分支的數目。 2.樹的路徑長度:樹中每個結點的路徑長度之和。 3.樹的帶權路徑長度:樹中所有葉子結點的帶權路徑長度之和WPL(T) = ∑wklk(對所有葉子結

資料結構知識整理 - 編碼

主要內容 基本概念 構造思路 儲存結構 構造演算法 哈夫曼編碼的引入 求哈夫曼編碼   基本概念 1)路徑:由一個結點到另一個結點之間的所有分支共同構成。 2)路徑長度:結點之間的分支數目。 3)樹的路徑長度:從樹的根

資料結構-2-編碼 原理詳解

 首先,介紹下什麼是哈夫曼樹。哈夫曼樹又稱最優二叉樹, 是一種帶權路徑長度最短的二叉樹。所謂樹的帶權路徑長度,就是樹中所有的葉結點 的權值乘上其到根結點的 路徑長度(若根結點為0層,葉結點到根結點的路徑長度 為葉結點的層數)。樹的帶權路徑長度記為WPL= (W1*L1+W

資料結構圖文解析之:編碼詳解及C++模板實現

0. 資料結構圖文解析系列 1. 哈夫曼編碼簡介 哈夫曼編碼(Huffman Coding)是一種編碼方式,也稱為“赫夫曼編碼”,是David A. Huffman1952年發明的一種構建極小多餘編碼的方法。 在計算機資料處理中,霍夫曼編碼使用變長編碼表對源符號進行編碼,出現頻率較高的源符號採用較短的編碼,

編碼(C語言程式碼實現)

在一般的資料結構的書中,樹的那章後面,著者一般都會介紹一下哈夫曼(HUFFMAN)樹和哈夫曼編碼。哈夫曼編碼是哈夫曼樹的一個應用。哈夫曼編碼應用廣泛,如 JPEG中就應用了哈夫曼編碼。 首先介紹什麼是哈夫曼樹。哈夫曼樹又稱最優二叉樹,是一種帶權路徑長度最短的二叉樹。所謂

#資料結構演算法學習筆記#PTA17:編碼 Huffman Tree & Huffman Code(C/C++)

2018.5.16 最近一段時間忙於實驗室各種專案和輔導員的各種雜活,間隔了半周沒有耐下心學習。導師最近接了一個要PK京東方的專案讓我來做總負責,確實是很驚喜了。責任心告訴我不能把工作做水了,但是還是嘗試把實權移交給師兄們比較好。 這道題可以說是樹這塊的壓軸題了,無論是程

HuffmanTree(赫)及編碼

今天帶領大家學一下哈夫曼 一. 概念: 赫夫曼樹又叫做最優二叉樹,它的特點是帶權路徑最短。 1)路徑:路徑是指從樹中一個結點到另一個結點的分支所構成的路線, 2)路徑長度:路徑長度是指路徑上的分支數目。 3)樹的路徑長度:樹的路徑長度是指從根到每個結點的路徑長度之和

php 二叉

二叉樹 赫夫曼樹 在學習圖之前,中間休息了兩天,感覺二叉樹需要消化一下。所以中間去溫習了下sql,推薦一本工具書《程序員的SQL金典》看名字不像一本好書,但是作為一個不錯的SQL工具書還是可以小小備忘一下。涵蓋內容不詳細但是挺廣,覆蓋多種主流數據庫言歸正傳,以前知道折半查找,二叉樹的概念也是感覺挺有意

完全二叉滿二叉

去筆試了很多次,每次都有有關於二叉樹的題目,而且其中最多的是關於完全二叉樹,然而完全二叉樹在哥心中的形態一直很模糊,究其原因是我把完全二叉樹和滿二叉樹搞混了。其實滿二叉樹是完全二叉樹的特例,因為滿二叉樹已經滿了,而完全並不代表滿。所以形態你也應該想象出來了吧,滿指的是出了

資料結構__赫及赫編碼_C++實現

#include<iostream> #include<stack> using namespace std; class NODE { public: NODE(); int weight,parent,lchild,rchild; }; NODE::NODE() { we

和赫編碼

赫夫曼樹,又稱最優樹,是一類帶權路徑長度最短樹。 從樹中一個節點到另一個節點之間的分支構成這兩個節點之間的路徑,路徑上的分支數目稱為路徑長度。樹的路徑長度指的是從樹根到樹中其他每個節點的路徑長度之和。節點的帶權路徑長度是指從樹的根節點到該節點之間的路徑長度與該節點上所帶權值

資料結構:赫和赫編碼的儲存表示

#include <stdio.h> #include <stdlib.h> #include <string.h> #define UINT_MAX 100 //假設各結點的權值不會超過100 typedef struct { unsigned int weigh

和霍編碼

#include <stdio.h> #include <stdlib.h> #include <cstring> using namespace std; typedef struct HuffNode { int weight;

C語言資料結構——赫和赫編碼

1、赫夫曼樹又稱最優樹,是一類帶權路徑長度最短的樹。 2、從樹的一個結點到另一個結點之間的分支構成這兩個結點之間的路徑,路徑上的分支數目稱為路徑長度。樹的路徑長度是從樹根到每個結點的路徑長度之和。

和赫編碼

問題:根據學生的百分制成績計算5級分製成績,成績在5個等級上的分佈規律: 分數 0-59 60-69 70-79 80-89 90-100 所佔比例 5% 15% 40% 30% 10% 圖二叉樹a中可以看出70分以上大約佔總數80%的成績都要經過3次以上的判斷才能得到結果

的實現以及編碼

基本概念 1、路徑和路徑長度 在一棵樹中,從一個結點往下可以達到的孩子或孫子結點之間的通路,稱為路徑。通路中分支的數目稱為路徑長度。若規定根結點的層數為1,則從根結點到第L層結點的路徑長度為L-1。 2、結點的權及帶權路徑長度 若將樹中結點賦給一個有著某

帶權路徑長度計算

假設我們一個權重為1,7,3,13,12,15,24怎麼樣畫出哈夫曼樹和計算帶權路徑長度。 首先,選出最小的兩個權重值,這裡是1,3(矩形表示葉子節點,圓表示根節點也是兩個葉子節點的和)如圖: 然後,選出第三小的7,算出父節點,如圖: 依次類推:   當11