1. 程式人生 > >Huffuman編碼與譯碼C語言實現

Huffuman編碼與譯碼C語言實現


一、Huffman編碼 

Huffuman編碼的過程主要分為兩步,第一步根據字元出現的權值構建Huffuman樹,第二步,遍歷Huffuman樹找出對應字元的編碼

1. 構建Huffuman樹程式碼如下:


typedef struct iNode {
        int weight;
        int parent, lchild, rchild;
} huffman_tree, *phuffman_tree;

int sum_weight(int *weight, int len)//計算權值之和
{
        int sum = 0;
        for(int i = 0; i < len; i++)
                sum += weight[i];
        return sum;
}

void select(huffman_tree *ht, int len, int &s1, int &s2)//搜尋ht陣列中parent為0的最小兩個節點,分別存入s1,s2,len為要搜尋的長度

{
        s1 = s2 = 0;
        for(int i = 1; i <= len; i++) {
                if(ht[i].parent == 0) {
                        if(ht[s1].weight >= ht[i].weight) {
                                s1 = i;
                                s2 = s1;
                        }
                }
        }
}

void make_tree(int *weight, huffman_tree* ht, int len)//weight是字元的權重,ht是儲存二叉樹的節點陣列
{
        int m = 2*len - 1;
        int s1 = 0, s2 = 0;
        ht[0] = {sun_weight(weight, len) + 1, 0, 0, 0};//0號節點權設定為總權值加1便於select時設定比較初值
        for(int i = 1; i <= len; i++)
                ht[i] = {weight[i - 1], 0, 0, 0};
        for( ; i <= m; i++)
                ht[i] = {0, 0, 0, 0};
        for(i = n + 1; i <= m; i++) {
                select(ht, i - 1, s1, s2);
                ht[s1].parent = i;
                ht[s2].parent = i;
                ht[i].lchild = s1;
                ht[i].rchild = s2;
                ht[i].weight = ht[s1].weight + ht[i].weight;
        }
}

1. 構建Huffuman編碼

 這裡有兩種構建方法,第一種是由葉子到根的構建,程式碼如下:

vodi make_code_from_leaf(huffman_tree *ht, int len, huffman_code *hc)//從葉子結點到根的編碼過程
{
        char code[len];
        //int start = len;
        //code[len - 1] = "\0";
        for (int i = 1; i <= len; i++) {
                int start = len;
                code[--start] = "\0";
                int next = i;
                while (ht[next].parent ! = 0) {
                        if (ht[ht[next].parent].lchild = next)
                                code[--start] = "0";
                        else
                                code[--start] = "1";
                        next = ht[next].parent;
                }
                strncpy(hc[i], &code[start], len - start);
        }
}

從根節點開始遍歷求huffuman編碼的程式碼如下(注意weight這裡發揮標記的作用:

void make_code_from_root(huffman_tree *ht, int len, huffman_code *hc)
{
        char code[len];
        int clen = 0;
        int p = 2*len - 1;
        for ( int i = 1; i <= p ; i++)
                ht[i].weight =  0;
        while (p) {
                if(ht[p].weight == 0) {//左節點遍歷
                        ht[p].weight = 1;
                        if(ht[p].lchild != 0) {
                                p = ht[p].lchild;
                                code[clen++] = "0";
                        }
                        else if(ht[p].rchild == 0) {
                                code[clen++] = "\0";
                                hc[p] = (char *)malloc(clen + 1);
                                strcpy([hc[p], code);
                        }


                }
                else if (ht[p].weight == 1) {//右子樹遍歷
                        ht[p].weight = 2;
                        if (ht[p].rchild != 0) {
                                code[clen++] = "1";
                                p = ht[p].rchild;
                        }
                }
                else { //到達葉子結點回溯另一分支
                        ht[p].weight = 0;
                        p = ht[p].parent;
                        clen--;


                }
        }
}

void make_code_from_root(huffman_tree *ht, int len, huffman_code *hc)
{
        char code[len];
        int clen = 0;
        int p = 2*len - 1;
        for ( int i = 1; i <= p ; i++)
                ht[i].weight =  0;
        while (p) {
                if(ht[p].weight == 0) {
                        ht[p].weight = 1;
                        if(ht[p].lchild != 0) {
                                p = ht[p].lchild;
                                code[clen++] = "0";
                        }
                        else if(ht[p].rchild == 0) {
                                code[clen++] = "\0";
                                hc[p] = (char *)malloc(clen + 1);
                                strcpy([hc[p], code);
                        }


                }
                else if (ht[p].weight == 1) {
                        ht[p].weight = 2;
                        if (ht[p].rchild != 0) {
                                code[clen++] = "1";
                                p = ht[p].rchild;
                        }
                }
                else {
                        ht[p].weight = 0;
                        p = ht[p].parent;
                        clen--;


                }
        }
}

void make_code_from_root(huffman_tree *ht, int len, huffman_code *hc)
{
        char code[len];
        int clen = 0;
        int p = 2*len - 1;
        for ( int i = 1; i <= p ; i++)
                ht[i].weight =  0;
        while (p) {
                if(ht[p].weight == 0) {
                        ht[p].weight = 1;
                        if(ht[p].lchild != 0) {
                                p = ht[p].lchild;
                                code[clen++] = "0";
                        }
                        else if(ht[p].rchild == 0) {
                                code[clen++] = "\0";
                                hc[p] = (char *)malloc(clen + 1);
                                strcpy([hc[p], code);
                        }


                }
                else if (ht[p].weight == 1) {
                        ht[p].weight = 2;
                        if (ht[p].rchild != 0) {
                                code[clen++] = "1";
                                p = ht[p].rchild;
                        }
                }
                else {
                        ht[p].weight = 0;
                        p = ht[p].parent;
                        clen--;


                }
        }
}

一、Huffman譯碼

根據Huffuman編碼規則的規則可知,如果所有左子樹代表“0”,右子樹代表“1”則,可以用搜索碼中的“0”來作為字元的分割碼,但是要注意最後的全1碼的識別其程式碼如下:

int select_hc(huffman_code *hc, huffman_code s_hc, int len)//根據獲得的編碼與Huffuman編碼表進行比較獲取該碼字在碼錶中的位置
{
        for (int i = 0; i < len; i++)
                if (strcmp(hc[i], s_hc) == 0)
                        return i;
        return -1;
}
/*******************encode function**********************
********************input : code is 0 an 1 serial*************************
********************hc is huffman code table, chars is the encoded characters, hc_len is char sum, len is code length****/
void decode(char *code, huffuman_code *hc, char *chars, char *encode_char, int hc_len, int len)//
{
        char temp_code[10];
        for (int i = 0, j = 0, k = 0; i < len; i++) {
                if (code[i] == "0") {
                        temp_code[k++] = "0";
                        temp_code[k++] = "\0";
                        int select_pos = select_hc(hc, temp_code, hc_len);
                        if (select_pps >= 0)
                                encode_char[j++] = chars[select_pos];
                        else
                                printf("error code : %s invalid", temp_code);
                        k = 0;
                }
                else {

temp_code[k++] = "1";

if (strcmp(temp_code,  hc[hc_len - 1]) == 0) {//注意處理“1”

temp_code[k++] = "\0";

encode_char[j++] = chars[hc_len - 1];

k = 0;

}

                       

}
        }
}