1. 程式人生 > >哈夫曼樹的建立和操作

哈夫曼樹的建立和操作

哈夫曼樹的引進是與帶有權重的二叉樹有關的
首先定義帶權路徑長度(WPL):設二叉樹有n個葉子結點,每個葉子結點帶有權值Wk,從根結點到每個葉子的長度為Ik,則每個葉子結點的帶權路徑長度之和就是:WPL=nk=1wklk
最優二叉樹哈夫曼樹:WPL最小的二叉樹

哈夫曼樹

那麼如何建立一棵哈夫曼樹呢,哈夫曼提出了一種方法,就是每次把權值最小的兩棵二叉樹合併,例如下圖所示

Huffman樹的例子

這就可以用到堆,每次把新生成的樹插入進堆,然後彈出兩個樹依次就是最小的樹,揭下來就是具體的實現了,首先是把之前的最小堆改為最大堆,然後把Elements的型別改掉,最後是寫Huffman樹

#include <stdio.h>    
#include <stdlib.h> #define MinData -1000; #define ERROR -1; /* 錯誤標識應根據具體情況定義為堆中不可能出現的元素值 */ struct TreeNode{ int Weight; TreeNode *Left; TreeNode *Right; }; typedef struct TreeNode *HuffmanTree; typedef HuffmanTree ElementType; struct HeapStruct{ ElementType *Elements;//儲存堆元素的陣列
int Size;//堆的當前元素個數 int Capacity;//堆的最大容量 }; typedef struct HeapStruct *MinHeap; //建立容量為MinSize的空的最小堆 MinHeap Create(int MinSize); //判斷最小堆是否已滿 int IsFull(MinHeap H); //最小堆的插入 void Insert(MinHeap &H,ElementType item); //判斷最小堆是否為空 int IsEmpty(MinHeap H); //從堆中刪除一個元素 ElementType DeleteMin(MinHeap H); //建造最小堆
void PercDown( MinHeap H, int p ); void BuildHeap( MinHeap H ); //列印堆中元素 void Print(MinHeap H); //建立Huffman樹 HuffmanTree Huffman(MinHeap H); //遍歷樹 void preOrder(HuffmanTree t); //先序遍歷 void intOrder(HuffmanTree t); //中序遍歷 void postOrder(HuffmanTree t); //後序遍歷 int main(){ MinHeap H= Create(15); int a[]={1,2,3,4,5}; int len=sizeof(a)/sizeof(int); H->Size=len; for(int i=0;i<len;i++) H->Elements[i+1]->Weight=a[i]; Print(H); HuffmanTree T; T=Huffman(H); printf("前序遍歷:"); preOrder(T) ; printf("\n"); printf("中序遍歷:"); intOrder(T); printf("\n"); printf("後序遍歷:"); postOrder(T); printf("\n"); system("pause"); } //建立容量為MinSize的空的最小堆 MinHeap Create(int MinSize){ MinHeap H=(MinHeap)malloc(sizeof(struct HeapStruct)); H->Elements=(ElementType *)malloc((MinSize+1)*sizeof(ElementType));//從小標為1的地方開始存放 //for(int i=0;i<=MinSize;i++) //H->Elements[i]=(HuffmanTree)malloc(sizeof(TreeNode)); for(int i=0;i<=MinSize;i++) { H->Elements[i]=(ElementType )malloc(sizeof(ElementType));//從小標為1的地方開始存放 H->Elements[i]->Weight=0;//定義“哨兵”為大於堆中所有可能元素的值,便於以後更快操作 H->Elements[i]->Left=NULL; H->Elements[i]->Right=NULL; } H->Size=0; H->Capacity=MinSize; H->Elements[0]->Weight=MinData;//定義“哨兵”為大於堆中所有可能元素的值,便於以後更快操作 return H; //時間複雜性是O(logN) }; //判斷最小堆是否已滿 int IsFull(MinHeap H){ return(H->Size==H->Capacity); }; //最小堆的插入 void Insert(MinHeap &H,ElementType item) {//將元素item插入最小堆H,其中H->Elements[0]已經定義為哨兵 int i; if(IsFull(H)){ printf("最小堆已滿\n"); return; } i=++H->Size;//先Size自加1再賦給i,i指向插入後堆中的最後一個元素 for(;H->Elements[i/2]->Weight>item->Weight;i/=2) H->Elements[i]=H->Elements[i/2];//向下過濾結點 //哨兵的作用就是避免插入的值比Elements[0]還小 H->Elements[i]=item;//將item插入,執行速度比交換快 //時間複雜性是O(logN) }; //判斷最小堆是否為空 int IsEmpty(MinHeap H){ return(H->Size==0); }; //從堆中刪除一個元素 ElementType DeleteMin(MinHeap H){//從最小堆H中取出鍵值最小的元素,並刪除一個結點 int Parent,Child; ElementType MinItem,temp; if(IsEmpty(H)){ printf("最小堆已為空\n"); return NULL; } MinItem=H->Elements[1];//取出根節點(最小值) //用最小堆中的最後一個元素從根節點開始向上過濾下層結點 temp=H->Elements[H->Size--]; for(Parent=1;Parent*2<=H->Size;Parent=Child){ Child=Parent*2; if((Child!=H->Size)&&(H->Elements[Child]->Weight>H->Elements[Child+1]->Weight)) Child++;//Child指向左右子結點中較小的 if(temp->Weight<=H->Elements[Child]->Weight)break; else//移動temp到下一層 H->Elements[Parent]=H->Elements[Child]; } H->Elements[Parent]=temp; return MinItem; } /*----------- 建造最小堆 -----------*/ void PercDown( MinHeap H, int p ) { /* 下濾:將H中以H->Data[p]為根的子堆調整為最小堆 */ int Parent, Child; ElementType X= H->Elements[p]; /* 取出根結點存放的值 */ for( Parent=p; Parent*2<=H->Size; Parent=Child ) { Child = Parent * 2; if( (Child!=H->Size) && (H->Elements[Child]->Weight>H->Elements[Child+1]->Weight) ) Child++; /* Child指向左右子結點的較小者 */ if( X->Weight <= H->Elements[Child]->Weight ) break; /* 找到了合適位置 */ else /* 下濾X */ H->Elements[Parent] = H->Elements[Child]; } H->Elements[Parent] = X; } void BuildHeap( MinHeap H ) { /* 調整H->Data[]中的元素,使滿足最小堆的有序性 */ /* 這裡假設所有H->Size個元素已經存在H->Data[]中 */ int i; /* 從最後一個結點的父節點開始,到根結點1 */ for( i = H->Size/2; i>0; i-- ) PercDown( H, i ); } //列印堆中元素 void Print(MinHeap H){ printf("H中的元素為:\n"); for(int i=1;i<=H->Size;i++) printf("%d ",H->Elements[i]->Weight); printf("\n"); } //建立Huffman樹 HuffmanTree Huffman(MinHeap H){ //假設H->Size個權值已經存在H->Elements[]->Weight裡 int i;HuffmanTree T; BuildHeap(H);//將堆調整為最小堆 int len=H->Size; for(i=1;i<len;i++){//做H->Size-1次合併 T=(HuffmanTree)malloc(sizeof(struct TreeNode));//建立新結點 T->Left=DeleteMin(H); T->Right=DeleteMin(H); T->Weight=T->Left->Weight+T->Right->Weight; Insert(H,T); } T=DeleteMin(H); return T; }; //遍歷樹 void preOrder(HuffmanTree t) //先序遍歷 { if(t) { printf("%d ",t->Weight); preOrder(t->Left); preOrder(t->Right); } } void intOrder(HuffmanTree t) //中序遍歷 { if(t) { intOrder(t->Left); printf("%d ",t->Weight); intOrder(t->Right); } } void postOrder(HuffmanTree t) //後序遍歷 { if(t) { postOrder(t->Left); postOrder(t->Right); printf("%d ",t->Weight); } }

重點有這麼兩個地方

MinHeap Create(int MinSize){  
    MinHeap H=(MinHeap)malloc(sizeof(struct HeapStruct));  
    H->Elements=(ElementType *)malloc((MinSize+1)*sizeof(ElementType));//從小標為1的地方開始存放

    //for(int i=0;i<=MinSize;i++)
        //H->Elements[i]=(HuffmanTree)malloc(sizeof(TreeNode));
    for(int i=0;i<=MinSize;i++)
    {
        H->Elements[i]=(ElementType )malloc(sizeof(ElementType));//從小標為1的地方開始存放
        H->Elements[i]->Weight=0;//定義“哨兵”為大於堆中所有可能元素的值,便於以後更快操作
        H->Elements[i]->Left=NULL;
        H->Elements[i]->Right=NULL;
    }

    H->Size=0;  
    H->Capacity=MinSize;  
    H->Elements[0]->Weight=MinData;//定義“哨兵”為大於堆中所有可能元素的值,便於以後更快操作  

    return H;  
    //時間複雜性是O(logN)  
}; 

一個是這裡建立時的malloc竟然有三次。。還是請同學幫我解決的

另外一個是下面Huffman樹的生成函式

HuffmanTree Huffman(MinHeap H){
    //假設H->Size個權值已經存在H->Elements[]->Weight裡
    int i;HuffmanTree T;
    BuildHeap(H);//將堆調整為最小堆
    int len=H->Size;
    for(i=1;i<len;i++){//做H->Size-1次合併
        T=(HuffmanTree)malloc(sizeof(struct TreeNode));//建立新結點
        T->Left=DeleteMin(H);
        T->Right=DeleteMin(H);
        T->Weight=T->Left->Weight+T->Right->Weight;
        Insert(H,T);
    }
    T=DeleteMin(H);
    return T;
};

下面是實現效果
這裡寫圖片描述

最後附上一段小程式碼

//整數轉化為樹之後再插入
void Insertint(MinHeap &H,int item){
    int i;
    if(IsFull(H)){
        printf("最小堆已滿\n");
        return;
    }
    i=++H->Size;//先Size自加1再賦給i,i指向插入後堆中的最後一個元素
    ElementType E1=(ElementType )malloc(sizeof(ElementType));
    E1->Right=E1->Left=NULL;
    E1->Weight=item;
    for(;H->Elements[i/2]->Weight>E1->Weight;i/=2)  
        H->Elements[i]=H->Elements[i/2];//向下過濾結點  
    //哨兵的作用就是避免插入的值比Elements[0]還小  
    H->Elements[i]=E1;//將item插入,執行速度比交換快  
    //時間複雜性是O(logN)  
}; 

這樣就可以呼叫

  MinHeap H= Create(15);

    Insertint(H,55);  
    Insertint(H,79);  
    Insertint(H,66);  
    Insertint(H,83);  
    Insertint(H,72);  
    Insertint(H,30);  
    Insertint(H,49);  
    Insertint(H,91);  
    Insertint(H,87);  
    Insertint(H,43);  
    Insertint(H,9);  
    Insertint(H,38);  
    Print(H);  
    DeleteMin(H);  
    DeleteMin(H);  
    Print(H);  

等語句,哦耶終於完成啦^^