程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> 應用C說話詳解霍夫曼樹數據構造

應用C說話詳解霍夫曼樹數據構造

編輯:關於C++

應用C說話詳解霍夫曼樹數據構造。本站提示廣大學習愛好者:(應用C說話詳解霍夫曼樹數據構造)文章只能為提供參考,不一定能成為您想要的結果。以下是應用C說話詳解霍夫曼樹數據構造正文


1、根本概念


a、途徑和途徑長度

若在一棵樹中存在著一個結點序列 k1,k2,……,kj, 使得 ki是ki+1 的雙親(1<=i<j),則稱此結點序列是從 k1 到 kj 的途徑。

從 k1 到 kj 所經由的分支數稱為這兩點之間的途徑長度,它等於途徑上的結點數減1.


b、結點的權和帶權途徑長度

在很多運用中,經常將樹中的結點付與一個有著某種意義的實數,我們稱此實數為該結點的權,(以下面一個樹中的藍色數字表現結點的權)

結點的帶權途徑長度劃定為從樹根結點到該結點之間的途徑長度與該結點上權的乘積。


c、樹的帶權途徑長度

樹的帶權途徑長度界說為樹中一切葉子結點的帶權途徑長度之和,公式為:

 個中,n表現葉子結點的數量,wi 和 li 分離表現葉子結點 ki 的權值和樹根結點到 ki 之間的途徑長度。

以下圖中樹的帶權途徑長度 WPL = 9 x 2 + 12 x 2 + 15 x 2 + 6 x 3 + 3 x 4 + 5 x 4  =  122

d、哈夫曼樹

哈夫曼樹又稱最優二叉樹。它是 n 個帶權葉子結點組成的一切二叉樹中,帶權途徑長度 WPL 最小的二叉樹。

以下圖為一哈夫曼樹表示圖。

2、結構哈夫曼樹


假定有n個權值,則結構出的哈夫曼樹有n個葉子結點。 n個權值分離設為 w1、w2、…、wn,則哈夫曼樹的結構規矩為:


(1) 將w1、w2、…,wn算作是有n 棵樹的叢林(每棵樹唯一一個結點);


(2) 在叢林當選出兩個根結點的權值最小的樹歸並,作為一棵新樹的左、右子樹,且新樹的根結點權值為其左、右子樹根結點權值之和;


(3)從叢林中刪除拔取的兩棵樹,並將新樹參加叢林;


(4)反復(2)、(3)步,直到叢林中只剩一棵樹為止,該樹即為所求得的哈夫曼樹。


 如:對 下圖中的六個帶權葉子結點來結構一棵哈夫曼樹,步調以下:

  留意:為了使獲得的哈夫曼樹的構造盡可能獨一,平日劃定生成的哈夫曼樹中每一個結點的左子樹根結點的權小於等於右子樹根結點的權。


詳細算法以下:

   

 //2、依據數組 a 中 n 個權值樹立一棵哈夫曼樹,前往樹根指針 
  struct BTreeNode* CreateHuffman(ElemType a[], int n) 
  { 
    int i, j; 
    struct BTreeNode **b, *q; 
    b = malloc(n*sizeof(struct BTreeNode)); 
    for (i = 0; i < n; i++) //初始化b指針數組,使每一個指針元素指向a數組中對應的元素結點 
    { 
      b[i] = malloc(sizeof(struct BTreeNode)); 
      b[i]->data = a[i]; 
      b[i]->left = b[i]->right = NULL; 
    } 
    for (i = 1; i < n; i++)//停止 n-1 次輪回樹立哈夫曼樹 
    { 
      //k1表現叢林中具有最小權值的樹根結點的下標,k2為次最小的下標 
      int k1 = -1, k2; 
      for (j = 0; j < n; j++)//讓k1初始指向叢林中第一棵樹,k2指向第二棵 
      { 
        if (b[j] != NULL && k1 == -1) 
        { 
          k1 = j; 
          continue; 
        } 
        if (b[j] != NULL) 
        { 
          k2 = j; 
          break; 
        } 
      } 
      for (j = k2; j < n; j++)//從以後叢林中求出最小權值樹和次最小 
      { 
        if (b[j] != NULL) 
        { 
          if (b[j]->data < b[k1]->data) 
          { 
            k2 = k1; 
            k1 = j; 
          } 
          else if (b[j]->data < b[k2]->data) 
            k2 = j; 
        } 
      } 
      //由最小權值樹和次最小權值樹樹立一棵新樹,q指向樹根結點 
      q = malloc(sizeof(struct BTreeNode)); 
      q->data = b[k1]->data + b[k2]->data; 
      q->left = b[k1]; 
      q->right = b[k2]; 
   
      b[k1] = q;//將指向新樹的指針賦給b指針數組中k1地位 
      b[k2] = NULL;//k2地位為空 
    } 
    free(b); //刪除靜態樹立的數組b 
    return q; //前往全部哈夫曼樹的樹根指針 
  } 


3、哈夫曼編碼

在電報通訊中,電文是以二進制的0、1序傳記送的,每一個字符對應一個二進制編碼,為了延長電文的總長度,采取不等長編碼方法,結構哈夫曼樹,

將每一個字符的湧現頻率作為字符結點的權值付與葉子結點,每一個分支結點的閣下分支分離用0和1編碼,從樹根結點到每一個葉子結點的途徑上

所經分支的0、1編碼序列等於該葉子結點的二進制編碼。如上文所示的哈夫曼編碼以下:

 a 的編碼為:00

b 的編碼為:01

c 的編碼為:100

d 的編碼為:1010

e 的編碼為:1011

f 的編碼為:11


4、哈夫曼樹的操作運算


以上文的哈夫曼樹作為詳細實例,用具體的法式展現哈夫曼樹的操作運算

 #include<stdio.h> 
 #include<stdlib.h> 
 typedef int ElemType; 
 struct BTreeNode 
 { 
  ElemType data; 
  struct BTreeNode* left; 
  struct BTreeNode* right; 
 }; 
  
 //1、輸入二叉樹,可在前序遍歷的基本上修正。采取狹義表格局,元素類型為int 
 void PrintBTree_int(struct BTreeNode* BT) 
 { 
  if (BT != NULL) 
  { 
   printf("%d", BT->data); //輸入根結點的值 
   if (BT->left != NULL || BT->right != NULL) 
   { 
    printf("("); 
    PrintBTree_int(BT->left); //輸入左子樹 
    if (BT->right != NULL) 
     printf(","); 
    PrintBTree_int(BT->right); //輸入右子樹 
    printf(")"); 
   } 
  } 
 } 
  
 //2、依據數組 a 中 n 個權值樹立一棵哈夫曼樹,前往樹根指針 
 struct BTreeNode* CreateHuffman(ElemType a[], int n) 
 { 
  int i, j; 
  struct BTreeNode **b, *q; 
  b = malloc(n*sizeof(struct BTreeNode)); 
  for (i = 0; i < n; i++) //初始化b指針數組,使每一個指針元素指向a數組中對應的元素結點 
  { 
   b[i] = malloc(sizeof(struct BTreeNode)); 
   b[i]->data = a[i]; 
   b[i]->left = b[i]->right = NULL; 
  } 
  for (i = 1; i < n; i++)//停止 n-1 次輪回樹立哈夫曼樹 
  { 
   //k1表現叢林中具有最小權值的樹根結點的下標,k2為次最小的下標 
   int k1 = -1, k2; 
   for (j = 0; j < n; j++)//讓k1初始指向叢林中第一棵樹,k2指向第二棵 
   { 
    if (b[j] != NULL && k1 == -1) 
    { 
     k1 = j; 
     continue; 
    } 
    if (b[j] != NULL) 
    { 
     k2 = j; 
     break; 
    } 
   } 
   for (j = k2; j < n; j++)//從以後叢林中求出最小權值樹和次最小 
   { 
    if (b[j] != NULL) 
    { 
     if (b[j]->data < b[k1]->data) 
     { 
      k2 = k1; 
      k1 = j; 
     } 
     else if (b[j]->data < b[k2]->data) 
      k2 = j; 
    } 
   } 
   //由最小權值樹和次最小權值樹樹立一棵新樹,q指向樹根結點 
   q = malloc(sizeof(struct BTreeNode)); 
   q->data = b[k1]->data + b[k2]->data; 
   q->left = b[k1]; 
   q->right = b[k2]; 
  
   b[k1] = q;//將指向新樹的指針賦給b指針數組中k1地位 
   b[k2] = NULL;//k2地位為空 
  } 
  free(b); //刪除靜態樹立的數組b 
  return q; //前往全部哈夫曼樹的樹根指針 
 } 
  
 //3、求哈夫曼樹的帶權途徑長度 
 ElemType WeightPathLength(struct BTreeNode* FBT, int len)//len初始為0 
 { 
  if (FBT == NULL) //空樹前往0 
   return 0; 
  else 
  { 
   if (FBT->left == NULL && FBT->right == NULL)//拜訪到葉子結點 
    return FBT->data * len; 
   else //拜訪到非葉子結點,停止遞歸挪用,前往閣下子樹的帶權途徑長度之和,len遞增 
    return WeightPathLength(FBT->left,len+1)+WeightPathLength(FBT->right,len+1); 
  } 
 } 
  
 //4、哈夫曼編碼(可以依據哈夫曼樹帶權途徑長度的算法基本長進行修正) 
 void HuffManCoding(struct BTreeNode* FBT, int len)//len初始值為0 
 { 
  static int a[10];//界說靜態數組a,保留每一個葉子的編碼,數組長度至多是樹深度減一 
  if (FBT != NULL)//拜訪到葉子結點時輸入其保留在數組a中的0和1序列編碼 
  { 
   if (FBT->left == NULL && FBT->right == NULL) 
   { 
    int i; 
    printf("結點權值為%d的編碼:", FBT->data); 
    for (i = 0; i < len; i++) 
     printf("%d", a[i]); 
    printf("\n"); 
   } 
   else//拜訪到非葉子結點時分離向閣下子樹遞歸挪用,並把分支上的0、1編碼保留到數組a 
   { //的對應元素中,向下深刻一層時len值增1 
    a[len] = 0; 
    HuffManCoding(FBT->left, len + 1); 
    a[len] = 1; 
    HuffManCoding(FBT->right, len + 1); 
   } 
  } 
 } 
  
 //主函數 
 void main() 
 { 
  int n, i; 
  ElemType* a; 
  struct BTreeNode* fbt; 
  printf("從鍵盤輸出待結構的哈夫曼樹中帶權葉子結點數n:"); 
  while(1) 
  { 
   scanf("%d", &n); 
   if (n > 1) 
    break; 
   else 
    printf("重輸n值:"); 
  } 
  a = malloc(n*sizeof(ElemType)); 
  printf("從鍵盤輸出%d個整數作為權值:", n); 
  for (i = 0; i < n; i++) 
   scanf(" %d", &a[i]); 
  fbt = CreateHuffman(a, n); 
  printf("狹義表情勢的哈夫曼樹:"); 
  PrintBTree_int(fbt); 
  printf("\n"); 
  printf("哈夫曼樹的帶權途徑長度:"); 
  printf("%d\n", WeightPathLength(fbt, 0)); 
  printf("樹中每一個葉子結點的哈夫曼編碼:\n"); 
  HuffManCoding(fbt, 0); 
 } 


運轉成果:

上面來看一道ACM標題

    標題描寫: 
    哈夫曼樹,第一行輸出一個數n,表現葉結點的個數。須要用這些葉結點生成哈夫曼樹,依據哈夫曼樹的概念,這些結點有權值,即weight,標題須要輸入一切結點的值與權值的乘積之和。 
    輸出: 
    輸出有多組數據。 
    每組第一行輸出一個數n,接著輸出n個葉節點(葉節點權值不跨越100,2<=n<=1000)。 
    輸入: 
    輸入權值。 
    樣例輸出: 
    5   
    1 2 2 5 9 
    樣例輸入: 
    37 


ac代碼

鏈表構建哈夫曼樹(拔出排序)

 #include <stdio.h> 
 #include <stdlib.h> 
 #define max 1001 
  
 struct htree 
 { 
  int weight; 
  struct htree *lchild; 
  struct htree *rchild; 
  struct htree *next; 
 }; 
  
 void addNode(struct htree *, struct htree *); 
 struct htree* createHfmtree(int *, int); 
 int getWpl(struct htree *, int); 
  
 int main() 
 { 
  int w[max]; 
  int i, n, wpl; 
  struct htree *ht; 
  
  while(scanf("%d", &n) != EOF) 
  { 
   for(i = 0; i < n; i ++) 
   { 
    scanf("%d", &w[i]); 
   } 
    
   ht = createHfmtree(w, n); 
   wpl = getWpl(ht, 0); 
   printf("%d\n", wpl); 
  } 
  return 0; 
 } 
  
 struct htree* createHfmtree(int *w, int n) 
 { 
  int i; 
  struct htree *head, *pl, *pr, *proot; 
  head = (struct htree *)malloc(sizeof(struct htree)); 
  head->next = NULL; 
  
  for(i = 0; i < n; i ++) 
  { 
   struct htree *pnode = malloc(sizeof(struct htree)); 
   pnode->weight = *(w + i); 
   pnode->lchild = pnode->rchild = pnode->next = NULL; 
   addNode(head, pnode); 
  } 
  
  while(head->next) 
  { 
   if(head->next->next == NULL) 
    break; 
   pl = head->next; 
   pr = pl->next; 
   head->next = pr->next; 
   proot = (struct htree *)malloc(sizeof(struct htree)); 
   proot->weight = pl->weight + pr->weight; 
   proot->lchild = pl; 
   proot->rchild = pr; 
   addNode(head, proot); 
  } 
  return head->next; 
 } 
  
 void addNode(struct htree *head, struct htree *pnode) 
 { 
  struct htree *t = head; 
  
  while(t->next && t->next->weight < pnode->weight) 
   t = t->next; 
  pnode->next = t->next; 
  t->next = pnode; 
 } 
  
 int getWpl(struct htree *ht, int level) 
 { 
  if(ht == NULL) 
   return 0; 
  if(!ht->lchild && !ht->rchild) 
  { 
   return ht->weight * level; 
  } 
  
  return getWpl(ht->lchild, level + 1) + getWpl(ht->rchild, level + 1); 
 } 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved