哈夫曼樹


這里講的哈夫曼樹有創建哈夫曼樹,輸出哈夫曼樹,遞歸進行哈夫曼樹編碼,哈夫曼解碼這些功能。

1.創建哈夫曼樹:(函數參數為整型數組)

(1)引入哈夫曼樹指針數組並申請空間,為每棵哈夫曼樹復制,將其左右節點賦值為NULL。

(2)將(n-1)棵哈夫曼樹合並:a.引入兩個整形變量始終代表最小和次小的下標

                                                    b.比較權值,使兩個下標成為最小的兩個權值的下標

                                                    c.合並一次,並將最小下標的哈夫曼樹賦值為新的哈夫曼樹,次小下表的哈夫曼樹賦值為空。

2.輸出哈夫曼樹:根節點不為空,輸出權重,若左右子樹不為空,依次輸出“(”, 左子樹,“,”,右子樹,“)”

3.遞歸進行哈夫曼編碼:由於哈夫曼樹的特點是所有的原始樹都為葉子節點,故可以用這方法來進行編碼。

4.哈夫曼解碼:while(已解碼長度<字符串長度){找到某一葉子節點並打印}

 

以下是完整代碼:

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

typedef int ELEMTYPE;

// 哈夫曼樹結點結構體

typedef struct HuffmanTree

{

    ELEMTYPE weight;

    ELEMTYPE id;        // id用來主要用以區分權值相同的結點,這里代表了下標

    struct HuffmanTree* lchild;

    struct HuffmanTree* rchild;

}HuffmanNode;

 

// 構建哈夫曼樹

HuffmanNode* createHuffmanTree(int* a, int n)

{

    int i, j;

    HuffmanNode **temp, *hufmTree;

    temp = (HuffmanNode **)malloc(n*sizeof(HuffmanNode));

    for (i=0; i<n; ++i)     // 將數組a中的權值賦給結點中的weight

    {

        temp[i] = (HuffmanNode*)malloc(sizeof(HuffmanNode));

        temp[i]->weight = a[i];

        temp[i]->id = i;

        temp[i]->lchild = temp[i]->rchild = NULL;

    }

 

    for (i=0; i<n-1; ++i)       // 構建哈夫曼樹需要n-1合並

    {

        int small1=-1, small2;      // small1small2分別作為最小和次小權值的下標

        for (j=0; j<n; ++j)         // 先將最小的兩個下標賦給small1small2(注意:對應權值未必最小)

        {

            if (temp[j] != NULL && small1==-1) 

            {

                small1 = j;

                continue;

            } else if(temp[j] != NULL)

            {

                small2 = j;

                break;

            }

        }

 

        for (j=small2; j<n; ++j)    // 比較權值,挪動small1small2使之分別成為最小和次小權值的下標

        {

            if (temp[j] != NULL)   

            {

                if (temp[j]->weight < temp[small1]->weight)

                {

                    small2 = small1;

                    small1 = j;

                } else if (temp[j]->weight < temp[small2]->weight)

                {

                    small2 = j;

                }

            }

        }

        hufmTree = (HuffmanNode*)malloc(sizeof(HuffmanNode));

        hufmTree->weight = temp[small1]->weight + temp[small2]->weight;

        hufmTree->lchild = temp[small1];

        hufmTree->rchild = temp[small2];

 

        temp[small1] = hufmTree;

        temp[small2] = NULL;

    }

    free(temp);

    return hufmTree;

}

 

// 以廣義表的形式打印哈夫曼樹

void PrintHuffmanTree(HuffmanNode* hufmTree)

{

    if (hufmTree)

    {

        printf("%d", hufmTree->weight);

        if (hufmTree->lchild != NULL || hufmTree->rchild != NULL)

        {

            printf("(");

            PrintHuffmanTree(hufmTree->lchild);

            printf(",");

            PrintHuffmanTree(hufmTree->rchild);

            printf(")");

        }

    }

}

 

// 遞歸進行哈夫曼編碼

void HuffmanCode(HuffmanNode* hufmTree, int depth)      // depth是哈夫曼樹的深度

{

    static int code[10];

    if (hufmTree)

    {

        if (hufmTree->lchild==NULL && hufmTree->rchild==NULL)

        {

            printf("id%d權值為%d的葉子結點的哈夫曼編碼為 ", hufmTree->id, hufmTree->weight);

            int i;

            for (i=0; i<depth; ++i)

            {

                printf("%d", code[i]);

            }

            printf("\n");

        } else

        {

            code[depth] = 0;

            HuffmanCode(hufmTree->lchild, depth+1);

            code[depth] = 1;

            HuffmanCode(hufmTree->rchild, depth+1);

        }

    }

}

 

// 哈夫曼解碼

void HuffmanDecode(char ch[], HuffmanNode* hufmTree, char string[])     // ch是要解碼的01串,string是結點對應的字符

{

    int i;

    int num[100];

    HuffmanNode* tempTree = NULL;

    for (i=0; i<strlen(ch); ++i)

    {

        if (ch[i] == '0')

            num[i] = 0;

        else

            num[i] = 1;

    }

    if(hufmTree)

    {

        i = 0;      // 計數已解碼01串的長度

        while(i<strlen(ch))

        {

            tempTree = hufmTree;

            while(tempTree->lchild!=NULL && tempTree->rchild!=NULL)

            {

                if (num[i] == 0)

                {

                    tempTree = tempTree->lchild;

                } else

                {

                    tempTree = tempTree->rchild;

                }

                ++i;

            }

            printf("%c", string[tempTree->id]);     // 輸出解碼后對應結點的字符

        }

    }

}

 

int main()

{

    int i, n;

    printf("請輸入葉子結點的個數:\n");

    while(1)

    {

        scanf("%d", &n);

        if (n>1)

            break;

        else

            printf("輸入錯誤,請重新輸入n值!");

    }

 

    int* arr;

    arr=(int*)malloc(n*sizeof(ELEMTYPE));

    printf("請輸入%d個葉子結點的權值:\n", n);

    for (i=0; i<n; ++i)

    {

        scanf("%d", &arr[i]);

    }

 

    char ch[100], string[100];

    printf("請連續輸入這%d個葉子結點各自所代表的字符:\n", n);

    fflush(stdin);      // 強行清除緩存中的數據,也就是上面輸入權值結束時的回車符

    gets(string);

 

    HuffmanNode* hufmTree = NULL;

    hufmTree = createHuffmanTree(arr, n);

 

    printf("此哈夫曼樹的廣義表形式為:\n");

    PrintHuffmanTree(hufmTree);

    printf("\n各葉子結點的哈夫曼編碼為:\n");

    HuffmanCode(hufmTree, 0);

 

    printf("要解碼嗎?請輸入編碼:\n");

    gets(ch);

    printf("解碼結果為:\n");

    HuffmanDecode(ch, hufmTree, string);

    printf("\n");

 

    free(arr);

    free(hufmTree);

 

    return 0;

}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM