1. 程式人生 > >最優二叉樹——霍夫曼樹

最優二叉樹——霍夫曼樹

一:什麼是最優二叉樹?

最優二叉樹就是從已給出的目標帶權結點(單獨的結點) 經過一種方式的組合形成一棵樹.使樹的權值最小. 最優二叉樹是帶權路徑長度最短的二叉樹。根據結點的個數,權值的不同,最優二叉樹的形狀也各不相同。它們的共同點是:帶權值的結點都是葉子結點。權值越小的結點,其到根結點的路徑越長。

官方定義:

在權為wl,w2,…,wn的n個葉子所構成的所有二叉樹中,帶權路徑長度最小(即代價最小)的二叉樹稱為最優二叉樹哈夫曼樹

二:下面先弄清幾個幾個概念:

1.路徑長度

在樹中從一個結點到另一個結點所經歷的分支構成了這兩個結點間的路徑上的分支數稱為它的路徑長度。

2.樹的路徑長度


     樹的路徑長度是從樹根到樹中每一結點的路徑長度之和。在結點數目相同的二叉樹中,完全二叉樹的路徑長度最短。
3.樹的帶權路徑長度(Weighted Path Length of Tree,簡記為WPL)
  結點的權
:在一些應用中,賦予樹中結點的一個有某種意義的實數。
  結點的帶權路徑長度:結點到樹根之間的路徑長度與該結點上權的乘積。
  樹的帶權路徑長度(Weighted Path Length of Tree):定義為樹中所有葉結點的帶權路徑長度之和,通常記為: 
                
  其中:
    n表示葉子結點的數目
    wi
li分別表示葉結點ki的權值和根到結點
ki之間的路徑長度。樹的帶權路徑長度亦稱為樹的代價。

三:用一個例子來理解一下以上概念

【例】給定4個葉子結點a,b,c和d,分別帶權7,5,2和4。構造如下圖所示的三棵二叉樹(還有許多棵),它們的帶權路徑長度分別為:


        (a)WPL=7*2+5*2+2*2+4*2=36
        (b)WPL=7*3+5*3+2*1+4*2=46
        (c)WPL=7*1+5*2+2*3+4*3=35

其中(c)樹的WPL最小,可以驗證,它就是哈夫曼樹。

注意:
    ① 葉子上的權值均相同時,完全二叉樹一定是最優二叉樹,否則完全二叉樹不一定是最優二叉樹。
    ② 最優二叉樹中,權越大的葉子離根越近。
    ③ 最優二叉樹的形態不唯一,WPL最小。

四.哈夫曼演算法

對於給定的葉子數目及其權值構造最優二叉樹的方法,由於這個演算法是哈夫曼提出來的,故稱其為哈夫曼演算法。其基本思想是:
  (1)根據給定的n個權值wl,w2,…,wn構成n棵二叉樹的森林F={T1,T2,…,Tn},其中每棵二叉樹Ti中都只有一個權值為wi的根結點,其左右子樹均空。
  (2)在森林F中選出兩棵根結點權值最小的樹(當這樣的樹不止兩棵樹時,可以從中任選兩棵),將這兩棵樹合併成一棵新樹,為了保證新樹仍是二叉樹,需 要增加一個新結點作為新樹的根,並將所選的兩棵樹的根分別作為新根的左右孩子(誰左,誰右無關緊要),將這兩個孩子的權值之和作為新樹根的權值。
  (3)對新的森林F重複(2),直到森林F中只剩下一棵樹為止。這棵樹便是哈夫曼樹。 
  
注意:
    ① 初始森林中的n棵二叉樹,每棵樹有一個孤立的結點,它們既是根,又是葉子
    ② n個葉子的哈夫曼樹要經過n-1次合併,產生n-1個新結點。最終求得的哈夫曼樹中共有2n-1個結點。
    ③ 哈夫曼樹是嚴格的二叉樹,沒有度數為1的分支結點。

五:最優二叉樹演算法具體實現思路

在構造哈夫曼樹時,可以設定一個結構陣列HuffNode儲存哈夫曼樹中各結點的資訊,根據二叉樹的性質可知,具有n個葉子結點的哈夫曼樹共有2n-1個結點,所以陣列HuffNode的大小設定為2n-1,陣列元素的結構形式如下:  

weight

lchild

rchild

parent

其中,weight域儲存結點的權值,lchild和rchild域分別儲存該結點的左、右孩子結點在陣列HuffNode中的序號,從而建立起結 點之間的關係。為了判定一個結點是否已加入到要建立的哈夫曼樹中,可通過parent域的值來確定。初始時parent的值為-1,當結點加入到樹中時, 該結點parent的值為其雙親結點在陣列HuffNode中的序號,就不會是-1了。構造哈夫曼樹時,首先將由n個字元形成的n個葉結點存放到陣列HuffNode的前n個分量中,然後根據前面介紹的哈夫曼方法的基本思想,不斷將兩個小子樹合併為一個較大的子樹,每次構成的新子樹的根結點順序放到HuffNode陣列中的前n個分量的後面。

/*霍夫曼編碼的C語言實現二*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#define MAXBIT 100
#define MAXVALUE 10000
#define MAXLEAF 30
#define MAXNODE MAXLEAF*2 -1
 
//編碼結構體
typedef struct 
{
    int bit[MAXBIT]; //MAXBIT = 100  最多存放100個編碼
    int start;
	
}HCodeType;        

//結點結構體
typedef struct
{
    int weight; //權
    int parent;  //父節點
    int lchild;  //左孩子節點
    int rchild;  //右孩子節點
    int value; //實際值
}HNodeType;        
 
/* 構造一顆哈夫曼樹 */
void HuffmanTree(HNodeType HuffNode[MAXNODE], int n)  //MAXNODE = 30*2 -1 = 59
{ 
    /* i、j: 迴圈變數,m1、m2:構造哈夫曼樹不同過程中兩個最小權值結點的權值,
      x1、x2:構造哈夫曼樹不同過程中兩個最小權值結點在陣列中的序號。*/
    int i, j, m1, m2, x1, x2;
	
    /* 初始化存放哈夫曼樹陣列 HuffNode[] 中的結點 */
    for (i=0; i<2*n-1; i++)
    {
        HuffNode[i].weight = 0;//權值 
        HuffNode[i].parent =-1;
        HuffNode[i].lchild =-1;
        HuffNode[i].rchild =-1;
        HuffNode[i].value=i; //實際值,可根據情況替換為字母  
    }
 
    //輸入 n 個葉子結點的權值 
    for (i=0; i<n; i++)
    {
        printf("輸入第%d個節點的權值:\n", i);
        scanf("%d", &HuffNode[i].weight);
    }
 
    /* 迴圈構造 Huffman 樹 */
    for (i=0; i<n-1; i++)
    {
        m1 = m2 = MAXVALUE; /* MAXVALUE = 10000 //m1、m2中存放兩個無父結點且結點權值最小的兩個結點 */
        x1 = x2 = 0;
		
        /* 找出所有結點中權值最小、無父結點的兩個結點,併合並之為一顆二叉樹 */
        for (j=0; j<n+i; j++)
        {
            if (HuffNode[j].weight < m1 && HuffNode[j].parent == -1)
            {
                m2 = m1; 
                x2 = x1; 
                m1 = HuffNode[j].weight;
                x1 = j;
            }
            else if (HuffNode[j].weight < m2 && HuffNode[j].parent==-1)
            {
                m2=HuffNode[j].weight;
                x2=j;
            }
        } /* end for */
            /* 設定找到的兩個子結點 x1、x2 的父結點資訊 */
        HuffNode[x1].parent  = n+i;
        HuffNode[x2].parent  = n+i;
        HuffNode[n+i].weight = HuffNode[x1].weight + HuffNode[x2].weight;
        HuffNode[n+i].lchild = x1;
        HuffNode[n+i].rchild = x2;
 
        printf ("第%d次==》左節點和右節點的權值為: %d, %d\n", i+1, HuffNode[x1].weight, HuffNode[x2].weight);  /* 用於測試 */
        printf ("\n");
    } /* end for */
  /*  for(i=0;i<n+2;i++)
    {
        printf(" Parents:%d,lchild:%d,rchild:%d,value:%d,weight:%d\n",HuffNode[i].parent,HuffNode[i].lchild,HuffNode[i].rchild,HuffNode[i].value,HuffNode[i].weight);
                  }*///測試 
} /* end HuffmanTree */
 
//解碼 
void decodeing(char string[],HNodeType Buf[],int Num)
{
  int i,tmp=0,code[1024];
  int m=2*Num-1;
  char *nump;
  char num[1024];
  for(i=0;i<strlen(string);i++)
  {
	if(string[i]=='0')
		num[i]=0;        
	else
		num[i]=1;                    
  } 
  
	i=0;
	nump=&num[0];
  
	while(nump<(&num[strlen(string)]))
	{
		tmp=m-1;
		while((Buf[tmp].lchild!=-1)&&(Buf[tmp].rchild!=-1))
		{
			if(*nump==0)
			{
				tmp=Buf[tmp].lchild ;          
			} 
			else 
			{
				tmp=Buf[tmp].rchild;
			}
		nump++;     
		} 
		printf("%d",Buf[tmp].value);                                  
	}
	printf("\n");
}
 
int main(void)
{ 
    HNodeType HuffNode[MAXNODE];            /* 定義一個結點結構體陣列 */
    HCodeType HuffCode[MAXLEAF],  cd;       /* 定義一個編碼結構體陣列, 同時定義一個臨時變數來存放求解編碼時的資訊 */
    int i, j, c, p, n;
    char pp[100];
    printf ("輸入編碼數量:");
    scanf ("%d", &n);
    HuffmanTree (HuffNode, n);
     
    for (i=1; i < n; i++)
    {
        cd.start = n-1;
        c = i;
        p = HuffNode[c].parent;
        while (p != -1)   /* 父結點存在 */
        {
            if (HuffNode[p].lchild == c)
                cd.bit[cd.start] = 0;
            else
                cd.bit[cd.start] = 1;
            cd.start--;        /* 求編碼的低一位 */
            c=p;                    
            p=HuffNode[c].parent;    /* 設定下一迴圈條件 */
        } /* end while */
        
        /* 儲存求出的每個葉結點的哈夫曼編碼和編碼的起始位 */
        for (j=cd.start+1; j<n; j++)
        { 
			HuffCode[i].bit[j] = cd.bit[j];
		}
        HuffCode[i].start = cd.start;
    } /* end for */
    
    /* 輸出已儲存好的所有存在編碼的哈夫曼編碼 */
    for (i=0; i<n; i++)
    {
        printf ("%d '的哈夫曼編碼是: ", i);
        for (j=HuffCode[i].start+1; j < n; j++)
        {
            printf ("%d", HuffCode[i].bit[j]);
        }
        printf(" start:%d",HuffCode[i].start);
        printf ("\n");  
    }
/*  for(i=0;i<n;i++)
	{
		for(j=0;j<n;j++)
		{
			printf ("%d", HuffCode[i].bit[j]);           
		}
        printf("\n");
    }*/
    printf("解碼(輸入二進位制編碼):\n");
    scanf("%s", pp);
	decodeing(pp, HuffNode, n);
    getchar();
	
    return 0;
}


相關推薦

——

一:什麼是最優二叉樹? 最優二叉樹就是從已給出的目標帶權結點(單獨的結點) 經過一種方式的組合形成一棵樹.使樹的權值最小. 最優二叉樹是帶權路徑長度最短的二叉樹。根據結點的個數,權值的不同,最優二叉樹的形狀也各不相同。它們的共同點是:帶權值的結點都是葉子結點。權值越小的

word2vec 中的數學原理 預備知識

append 哈夫曼編碼 har html ant 世界 word2vec tree plus 主要參考: word2vec 中的數學原理詳解 自己動手寫 word2vec 編碼的話,根是不記錄在編碼中的 這一篇主要講的就是

)簡介

一、霍夫曼編碼       說到霍夫曼樹,就不得不提霍夫曼編碼(Huffman Coding)。霍夫曼編碼是可變字長編碼(VLC)的一種。David.A.Huffman於1952年提出該編碼方法,即完

(赫)的構建

一、構建最優二叉樹 ①、節點類:五個屬性:結點的資料、父結點、左子結點、右子結點、赫夫曼編碼 /** * 樹的結點類 * * @author lenovo * */ public class TreeNode { private Object obj;

編碼(基於哈-,不唯一)、B(b-)、B+

整合自: http://blog.csdn.net/shuangde800/article/details/7341289 http://www.cnblogs.com/Jezze/archive/2011/12/23/2299884.html http:/

之赫

一、介紹 赫夫曼樹可以用來優化演算法,減少執行次數;還可以用於電報編碼。 二、優化演算法 對於我們巢狀的if語句,通常我們需要判斷多次if。但是if裡表示式的順序與程式執行速度有很大關係。如果我們把頻率高的if條件放在前面的話就能夠避免多次檢測fa

資料結構之哈

文字壓縮是一種非常重要的技術,自然涉及到了壓縮編碼。哈夫曼編碼——一種最基本的壓縮編碼方法 幾個術語: 1、路徑:樹中兩個節點之間的分支序列 2、路徑長度:路徑上的分支數目

)的構造【的應用】

        對於給定一個長度為m序列,構造一顆以序列值為權的m個外部結點的擴充二叉樹,使得帶權的外部路徑長度WPL最小,就稱這顆擴充二叉樹為 哈夫曼(Huffman)樹(最優二叉樹)。構造Huffman Tree 的演算法也就是哈夫曼演算法。演算法基本思想:1)給定m個權

之哈

        本文來介紹哈夫曼樹。哈夫曼樹又叫最優二叉樹,是一種特殊的二叉樹。這種二叉樹最重要的特徵就是:樹的帶權路徑長度(Weighted Path Length of Tree,簡記為WPL)最小。本文給出了哈弗曼演算法的實現過程,程式碼部分已經描述的比較詳細,這裡就

(哈)知識點

路徑:在一棵樹中從一個結點往下到孩子或孫子結點之間的通路 結點的路徑長度:從根節點到該節點的路徑上分支的數目 樹的路徑長度:樹中每個結點的路徑長度之和 結點的權:給樹中的結點賦予一個某種含義的值,則該

最優二叉樹,也稱哈夫曼(Haffman)樹,是指對於一組帶有確定權值的葉結點,構造的具有最小帶權路徑長度的二叉樹。 二叉樹的路徑長度則是指由根結點到所有葉結點的路徑長度之和。如果二叉樹中的葉結點都具有一定的權值,則可將這一概念加以推廣。設二叉樹具有n個帶權值的葉結點,那麼從

滿、完全(赫)、排序判定

二叉排序樹(Binary Sort Tree)又稱二叉查詢樹。 它或者是一棵空樹;或者是具有下列性質的二叉樹: (1)若左子樹不空,則左子樹上所有結點的值均小於它的根結點的值; (2)若右子樹不空,則右子樹上所有結點的值均大於它的根結點的值; (3)左、右子樹也分別為二叉排序樹;

——哈

一:什麼是最優二叉樹? 從我個人理解來說,最優二叉樹就是從已給出的目標帶權結點(單獨的結點) 經過一種方式的組合形成一棵樹.使樹的權值最小. 最優二叉樹是帶權路徑長度最短的二叉樹。根據結點的個數,權值的不同,最優二叉樹的形狀也各不相同。它們的共同點是:帶權值的結點都是葉

完成基於哈)的壓縮及解壓小程式的收穫

收穫      1) 更有條理的構造我的程式碼了: 先從main方法下手,將自己想要的實現程式的功能以註釋 的方式寫出來,然後再逐漸細化每一部分的功能,每部分的功能都有非常明確的輸入部分,將這些輸入的內容加工,進行輸出(也就是下一部分功能的實現的輸入部分)就是這部分功能

資料結構實驗之六:哈編碼(

Problem Description 字元的編碼方式有多種,除了大家熟悉的ASCII編碼,哈夫曼編碼(Huffman Coding)也是一種編碼方式,它是可變字長編碼。該方法完全依據字元出現概率來構造出平均長度最短的編碼,稱之為最優編碼。哈夫曼編碼常被用於資

一本正經的聊資料結構(6): —— 哈

![](https://cdn.geekdigging.com/DataStructure/head.png) 前文傳送門: [「一本正經的聊資料結構(1):時間複雜度」](https://www.geekdigging.com/2020/03/28/6072951828/) [「一本正經的聊資料結構(

解碼 合並 這樣的 define 又是 當前 對數 左右子樹 選擇 最優二叉樹 1.樹的路徑長度   樹的路徑長度是從樹根到樹中每一結點的路徑長度之和。在結點數目相同的二叉樹中,完全二叉樹的路徑長度最短。 2.樹的帶權路徑長度(Weighted Path Lengt

完全,滿

霍夫曼樹:每個節點要嘛沒有子節點,要麼有兩個子節點 完全二叉樹:滿二叉樹的一部分或者全部。 滿二叉樹:每個父親都有2個葉子。 1 1 / \

完全與滿

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

、赫爾(學習記錄)

注意vector儲存的記憶體地址可能會變,所以如果用vector來儲存所有節點的話,需要先resize好容器,保證期間不進行節點的增減。multiset是允許關鍵字重複的容器 #include<