Huffman编码解码

转载自:http://blog.csdn.net/q547550831/article/details/51589278

Huffman编码解码

霍夫曼(Huffman)编码问题也就是最优编码问题,通过比较权值逐步构建一颗Huffman树,再由Huffman树进行编码、解码。

其步骤是先构建一个包含所有节点的线性表,每次选取最小权值的两个节点,生成一个父亲节点,该父亲节点的权值等于两节点权值之和,然后将该父亲节点加入到该线性表中,再重复上述步骤,直至构成一个二叉树,注意已经使用过的节点不参与。

Huffman编码贪心原理

编码原理

把每个字符看作一个单节点子树放在一个树集合中,每棵子树的权值等于相应字符的频率。每次取权值最小的两棵子树合成一棵新树,并重新放到集合中。新树的权值等于两棵子树权值之和。

贪心选择性

x y 是频率最小的两个字符,则存在前缀码使得 x y 具有相同码长,且仅有最后一位编码不同。换句话说,贪心选择保留了最优解。

优化子结构

T 是加权字符集 C 的最优编码树, x y 是树 T 中两个叶子,且互为兄弟结点, z 是它们的父结点。若把 z 看成具有频率 f(z)=f(x)+f(y) 的字符,则树 T=T{x,y} 是字符集 C=C{x,y}{z} 的一棵最优编码树。换句话说,原问题的最优解包含子问题的最优解。

举例说明

编码表

字符 a b c d e f
频率 45 13 12 16 9 5
编码 0 101 100 111 1101 1100

Huffman编码

下面将解释为什么是这样编码,在解释之前先说明一个概念:

  • 前缀码:任何一个编码都不是另一个编码的前缀(prefix)。

如果Huffman编码符合前缀码的要求的话,那么绝不会出现编码二义性的问题。而且通过权值这一参考量,构成了最优编码。

原理图

原始节点


构建Huffman树的步骤


Huffman编码

Huffman编码解码算法实现

节点信息结构

// 节点信息结构
struct Node {
    // 值
    string value;
    // 权值
    float weight;
    // 父节点
    int parent;
    // 左子节点
    int lchild;
    // 右子节点
    int rchild;
};

编码信息结构

// 编码信息结构
struct Code {
    // 编码字符
    int bit[maxBit];
    // 开始位置
    int start;
    // 值
    string value;
};

全局常量和全局变量

const int INF = 1000000000;
const int maxBit = 1 << 5;
const int maxNode = 1 << 10;
const int maxCode = 1 << 10;

// 节点数组
Node huffman[maxNode];
// 编码数组
Code huffmanCode[maxCode];
// n个字符串
int n;

初始化Huffman树

// 初始化Huffman树
void initHuffmanTree() {
    for(int i = 0; i < (2 * n) - 1; i++) {
        huffman[i].weight = 0;
        huffman[i].value = "";
        huffman[i].parent = -1;
        huffman[i].lchild = -1;
        huffman[i].rchild = -1;
    }
}

构造Huffman树

// 贪心法
// 构造Huffman树
void huffmanTree() {
    // 循环构建Huffman树
    for(int i = 0; i < n - 1; i++) {
        // m1,m2存放所有节点中权值最小的两个节点权值
        int m1 = INF;
        int m2 = INF;
        // x1,x2存放所有节点中权值最小的两个节点下标
        int x1 = 0;
        int x2 = 0;
        for(int j = 0; j < n + i; j++) {
            if(huffman[j].weight < m1 && huffman[j].parent == -1) {
                m2 = m1;
                x2 = x1;
                m1 = huffman[j].weight;
                x1 = j;
            } else if(huffman[j].weight < m2 && huffman[j].parent == -1) {
                m2 = huffman[j].weight;
                x2 = j;
            }
        }
        // 设置找到的两个节点的x1,x2的父节点信息
        huffman[x1].parent = n + i;
        huffman[x2].parent = n + i;
        huffman[n + i].weight = huffman[x1].weight + huffman[x2].weight;
        huffman[n + i].lchild = x1;
        huffman[n + i].rchild = x2;
    }
}

Huffman编码

// huffman编码
void huffmanEncoding() {
    // 临时结构
    Code cd;
    int child, parent;
    for(int i = 0; i < n; i++) {
        cd.value = huffman[i].value;
        cd.start = n - 1;
        child = i;
        parent = huffman[child].parent;
        // 未到根节点
        while(parent != -1) {
            // 左孩子
            if(huffman[parent].lchild == child) {
                cd.bit[cd.start] = 0;
            } else {
                // 右孩子
                cd.bit[cd.start] = 1;
            }
            cd.start--;
            // 设置下一循环条件
            child = parent;
            parent = huffman[child].parent;
        }

        // 保存求出的每个叶子节点的Huffman编码结构
        for(int j = cd.start + 1; j < n; j++) {
            huffmanCode[i].bit[j] = cd.bit[j];
        }
        huffmanCode[i].start = cd.start;
        huffmanCode[i].value = cd.value;
    }
}

打印Huffman编码信息

// 打印每个叶节点的Huffman编码和编码起始值
void printHuffmanCode() {
    for(int i = 0; i < n; i++) {
        cout << "第" << i + 1 << "个字符 " << huffmanCode[i].value << " 的Huffman编码为:";
        for(int j = huffmanCode[i].start + 1; j < n; j++) {
            cout << huffmanCode[i].bit[j];
        }
        cout << " 编码起始值为:" << huffmanCode[i].start << endl;
    }
    cout << endl;
}

解码Huffman编码

// 解码Huffman编码
void HuffmanDecoding(string s) {
    vector<string> v;
    // 标识位
    int ok = 1;
    for(int i = 0; i < s.length();) {
        // 根节点
        int x = (2 * n) - 1 - 1;
        // 不为叶子节点
        while(huffman[x].lchild != -1 && huffman[x].rchild != -1) {
            // 左子树
            if(s[i] == '0') {
                x = huffman[x].lchild;
            } else {
                // 右子树
                x = huffman[x].rchild;
            }
            i++;
            // 处理0,1序列有误
            // 这种情况一般是结尾0,1序列少了,导致最后一个字符串解码失败
            if(i == s.length() && huffman[x].lchild != -1) {
                ok = 0;
                break;
            }
        }

        if(ok) {
            v.push_back(huffman[x].value);
        }
    }
    if(ok) {
        for(int i = 0; i < v.size(); i++) {
            cout << v[i];
        }
        cout << endl << endl;
    } else {
        cout << "解码有误。" << endl << endl;
    }
}

主函数

int main() {
    while(true) {
        // 初始化

        // 输入数据
        cout << "请输入字符串个数(0退出):";
        cin >> n;
        if(!n) {
            break;
        }

        // 初始化Huffman树
        initHuffmanTree();

        for(int i = 0; i < n; i++) {
            cout << "一共" << n << "个字符串,请输入第" << i + 1 << "个字符串及其权值:";
            cin >> huffman[i].value;
            cin >> huffman[i].weight;
        }

        // 构造Huffman树
        huffmanTree();

        // huffman编码
        huffmanEncoding();

        // 打印每个叶节点的Huffman编码和编码起始值
        printHuffmanCode();

        while(true) {
            cout << "请输入一段符合上述编码的0,1序列(q进入下一次编码解码):";
            string s;
            cin >> s;
            if(s[0] == 'q') {
                cout << endl;
                break;
            }
            cout << "原始0,1序列为:" << s << endl;
            cout << "解码后为:";
            // 解码
            HuffmanDecoding(s);
        }
    }
    return 0;
}

测试主程序

#include <iostream>
#include <vector>
#include <string>

using namespace std;

const int INF = 1000000000;
const int maxBit = 1 << 5;
const int maxNode = 1 << 10;
const int maxCode = 1 << 10;

// 节点信息结构
struct Node {
    // 值
    string value;
    // 权值
    float weight;
    // 父节点
    int parent;
    // 左子节点
    int lchild;
    // 右子节点
    int rchild;
};

// 编码信息结构
struct Code {
    // 编码字符
    int bit[maxBit];
    // 开始位置
    int start;
    // 值
    string value;
};

// 节点数组
Node huffman[maxNode];
// 编码数组
Code huffmanCode[maxCode];

// n个字符串
int n;

// 初始化Huffman树
void initHuffmanTree() {
    for(int i = 0; i < (2 * n) - 1; i++) {
        huffman[i].weight = 0;
        huffman[i].value = "";
        huffman[i].parent = -1;
        huffman[i].lchild = -1;
        huffman[i].rchild = -1;
    }
}


// 贪心法
// 构造Huffman树
void huffmanTree() {
    // 循环构建Huffman树
    for(int i = 0; i < n - 1; i++) {
        // m1,m2存放所有节点中权值最小的两个节点权值
        int m1 = INF;
        int m2 = INF;
        // x1,x2存放所有节点中权值最小的两个节点下标
        int x1 = 0;
        int x2 = 0;
        for(int j = 0; j < n + i; j++) {
            if(huffman[j].weight < m1 && huffman[j].parent == -1) {
                m2 = m1;
                x2 = x1;
                m1 = huffman[j].weight;
                x1 = j;
            } else if(huffman[j].weight < m2 && huffman[j].parent == -1) {
                m2 = huffman[j].weight;
                x2 = j;
            }
        }
        // 设置找到的两个节点的x1,x2的父节点信息
        huffman[x1].parent = n + i;
        huffman[x2].parent = n + i;
        huffman[n + i].weight = huffman[x1].weight + huffman[x2].weight;
        huffman[n + i].lchild = x1;
        huffman[n + i].rchild = x2;
    }
}

// huffman编码
void huffmanEncoding() {
    // 临时结构
    Code cd;
    int child, parent;
    for(int i = 0; i < n; i++) {
        cd.value = huffman[i].value;
        cd.start = n - 1;
        child = i;
        parent = huffman[child].parent;
        // 未到根节点
        while(parent != -1) {
            // 左孩子
            if(huffman[parent].lchild == child) {
                cd.bit[cd.start] = 0;
            } else {
                // 右孩子
                cd.bit[cd.start] = 1;
            }
            cd.start--;
            // 设置下一循环条件
            child = parent;
            parent = huffman[child].parent;
        }

        // 保存求出的每个叶子节点的Huffman编码结构
        for(int j = cd.start + 1; j < n; j++) {
            huffmanCode[i].bit[j] = cd.bit[j];
        }
        huffmanCode[i].start = cd.start;
        huffmanCode[i].value = cd.value;
    }
}

// 打印每个叶节点的Huffman编码和编码起始值
void printHuffmanCode() {
    for(int i = 0; i < n; i++) {
        cout << "第" << i + 1 << "个字符 " << huffmanCode[i].value << " 的Huffman编码为:";
        for(int j = huffmanCode[i].start + 1; j < n; j++) {
            cout << huffmanCode[i].bit[j];
        }
        cout << " 编码起始值为:" << huffmanCode[i].start << endl;
    }
    cout << endl;
}

// 解码Huffman编码
void HuffmanDecoding(string s) {
    vector<string> v;
    // 标识位
    int ok = 1;
    for(int i = 0; i < s.length();) {
        // 根节点
        int x = (2 * n) - 1 - 1;
        // 不为叶子节点
        while(huffman[x].lchild != -1 && huffman[x].rchild != -1) {
            // 左子树
            if(s[i] == '0') {
                x = huffman[x].lchild;
            } else {
                // 右子树
                x = huffman[x].rchild;
            }
            i++;
            // 处理0,1序列有误
            // 这种情况一般是结尾0,1序列少了,导致最后一个字符串解码失败
            if(i == s.length() && huffman[x].lchild != -1) {
                ok = 0;
                break;
            }
        }

        if(ok) {
            v.push_back(huffman[x].value);
        }
    }
    if(ok) {
        for(int i = 0; i < v.size(); i++) {
            cout << v[i];
        }
        cout << endl << endl;
    } else {
        cout << "解码有误。" << endl << endl;
    }
}

int main() {
    while(true) {
        // 初始化

        // 输入数据
        cout << "请输入字符串个数(0退出):";
        cin >> n;
        if(!n) {
            break;
        }

        // 初始化Huffman树
        initHuffmanTree();

        for(int i = 0; i < n; i++) {
            cout << "一共" << n << "个字符串,请输入第" << i + 1 << "个字符串及其权值:";
            cin >> huffman[i].value;
            cin >> huffman[i].weight;
        }

        // 构造Huffman树
        huffmanTree();

        // huffman编码
        huffmanEncoding();

        // 打印每个叶节点的Huffman编码和编码起始值
        printHuffmanCode();

        while(true) {
            cout << "请输入一段符合上述编码的0,1序列(q进入下一次编码解码):";
            string s;
            cin >> s;
            if(s[0] == 'q') {
                cout << endl;
                break;
            }
            cout << "原始0,1序列为:" << s << endl;
            cout << "解码后为:";
            // 解码
            HuffmanDecoding(s);
        }
    }
    return 0;
}

输出数据

请输入字符串个数(0退出):6
一共6个字符串,请输入第1个字符串及其权值:a 45
一共6个字符串,请输入第2个字符串及其权值:b 13
一共6个字符串,请输入第3个字符串及其权值:c 12
一共6个字符串,请输入第4个字符串及其权值:d 16
一共6个字符串,请输入第5个字符串及其权值:e 9
一共6个字符串,请输入第6个字符串及其权值:f 5
第1个字符 a 的Huffman编码为:0 编码起始值为:4
第2个字符 b 的Huffman编码为:101 编码起始值为:2
第3个字符 c 的Huffman编码为:100 编码起始值为:2
第4个字符 d 的Huffman编码为:111 编码起始值为:2
第5个字符 e 的Huffman编码为:1101 编码起始值为:1
第6个字符 f 的Huffman编码为:1100 编码起始值为:1

请输入一段符合上述编码的0,1序列(q进入下一次编码解码):010011110111011100
原始0,1序列为:010011110111011100
解码后为:acdbef

请输入一段符合上述编码的0,1序列(q进入下一次编码解码):00010010110010111011101110011001111110101
原始0,1序列为:00010010110010111011101110011001111110101
解码后为:aaacbcbeeffddab

请输入一段符合上述编码的0,1序列(q进入下一次编码解码):010110011
原始0,1序列为:010110011
解码后为:解码有误。

请输入一段符合上述编码的0,1序列(q进入下一次编码解码):q

请输入字符串个数(0退出):0

Process returned 0 (0x0)   execution time : 16.174 s
Press any key to continue.



















Huffman编码是一种无损压缩算法,常用于数据传输和存储中。下面是C语言实现Huffman编码解码的基本思路: Huffman编码: 1. 统计字符出现频率,构建字符频率表; 2. 将字符频率表中的字符和频率信息构建成一棵Huffman树; 3. 遍历Huffman树,给每个字符生成对应的Huffman编码; 4. 将编码写入文件。 Huffman解码: 1. 读取压缩文件中的编码信息和字符频率表; 2. 根据字符频率表构建Huffman树; 3. 遍历Huffman树,根据编码信息还原原始字符串。 以下是C语言实现Huffman编码解码的参考代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_TREE_HT 100 // Huffman树节点结构体 struct MinHeapNode { char data; unsigned freq; struct MinHeapNode *left, *right; }; // Huffman编码表结构体 struct HuffmanCode { char data; char *code; }; // 最小优先队列结构体 struct MinHeap { unsigned size; unsigned capacity; struct MinHeapNode **array; }; // 创建一个新的Huffman树节点 struct MinHeapNode* newNode(char data, unsigned freq) { struct MinHeapNode* node = (struct MinHeapNode*)malloc(sizeof(struct MinHeapNode)); node->left = node->right = NULL; node->data = data; node->freq = freq; return node; } // 创建一个最小优先队列 struct MinHeap* createMinHeap(unsigned capacity) { struct MinHeap* minHeap = (struct MinHeap*)malloc(sizeof(struct MinHeap)); minHeap->size = 0; minHeap->capacity = capacity; minHeap->array = (struct MinHeapNode**)malloc(minHeap->capacity * sizeof(struct MinHeapNode*)); return minHeap; } // 交换两个节点 void swapMinHeapNode(struct MinHeapNode** a, struct MinHeapNode** b) { struct MinHeapNode* t = *a; *a = *b; *b = t; } // 最小堆化 void minHeapify(struct MinHeap* minHeap, int idx) { int smallest = idx; int left = 2 * idx + 1; int right = 2 * idx + 2; if (left < minHeap->size && minHeap->array[left]->freq < minHeap->array[smallest]->freq) smallest = left; if (right < minHeap->size && minHeap->array[right]->freq < minHeap->array[smallest]->freq) smallest = right; if (smallest != idx) { swapMinHeapNode(&minHeap->array[smallest], &minHeap->array[idx]); minHeapify(minHeap, smallest); } } // 检查堆大小是否为1 int isSizeOne(struct MinHeap* minHeap) { return (minHeap->size == 1); } // 取出堆顶元素 struct MinHeapNode* extractMin(struct MinHeap* minHeap) { struct MinHeapNode* temp = minHeap->array[0]; minHeap->array[0] = minHeap->array[minHeap->size - 1]; --minHeap->size; minHeapify(minHeap, 0); return temp; } // 插入新的节点 void insertMinHeap(struct MinHeap* minHeap, struct MinHeapNode* minHeapNode) { ++minHeap->size; int i = minHeap->size - 1; while (i && minHeapNode->freq < minHeap->array[(i - 1) / 2]->freq) { minHeap->array[i] = minHeap->array[(i - 1) / 2]; i = (i - 1) / 2; } minHeap->array[i] = minHeapNode; } // 构建最小优先队列 void buildMinHeap(struct MinHeap* minHeap) { int n = minHeap->size - 1; int i; for (i = (n - 1) / 2; i >= 0; --i) minHeapify(minHeap, i); } // 判断是否是叶子节点 int isLeaf(struct MinHeapNode* root) { return !(root->left) && !(root->right); } // 创建一个最小优先队列并构建Huffman树 struct MinHeap* createAndBuildMinHeap(char data[], int freq[], int size) { int i; struct MinHeap* minHeap = createMinHeap(size); for (i = 0; i < size; ++i) minHeap->array[i] = newNode(data[i], freq[i]); minHeap->size = size; buildMinHeap(minHeap); return minHeap; } // 构建Huffman树 struct MinHeapNode* buildHuffmanTree(char data[], int freq[], int size) { struct MinHeapNode *left, *right, *top; struct MinHeap* minHeap = createAndBuildMinHeap(data, freq, size); while (!isSizeOne(minHeap)) { left = extractMin(minHeap); right = extractMin(minHeap); top = newNode('$', left->freq + right->freq); top->left = left; top->right = right; insertMinHeap(minHeap, top); } return extractMin(minHeap); } // 生成Huffman编码 void generateCodes(struct MinHeapNode* root, char *code, int top, struct HuffmanCode *huffmanCode) { if (root->left) { code[top] = '0'; generateCodes(root->left, code, top + 1, huffmanCode); } if (root->right) { code[top] = '1'; generateCodes(root->right, code, top + 1, huffmanCode); } if (isLeaf(root)) { huffmanCode[root->data].data = root->data; huffmanCode[root->data].code = (char*)malloc((top + 1) * sizeof(char)); memcpy(huffmanCode[root->data].code, code, (top + 1) * sizeof(char)); } } // 打印Huffman编码表 void printHuffmanCodes(struct HuffmanCode *huffmanCode, int size) { int i; printf("Huffman编码表:\n"); for (i = 0; i < size; ++i) printf("%c: %s\n", huffmanCode[i].data, huffmanCode[i].code); } // 压缩文件 void compressFile(char *fileName, struct HuffmanCode *huffmanCode, int size) { FILE *fpIn, *fpOut; char ch, *code; int i, j, len; fpIn = fopen(fileName, "r"); if (fpIn == NULL) { printf("打开文件失败!\n"); return; } fpOut = fopen("compressed.dat", "wb"); fwrite(&size, sizeof(int), 1, fpOut); for (i = 0; i < size; ++i) { fwrite(&huffmanCode[i].data, sizeof(char), 1, fpOut); len = strlen(huffmanCode[i].code); fwrite(&len, sizeof(int), 1, fpOut); fwrite(huffmanCode[i].code, sizeof(char), len, fpOut); } code = (char*)malloc(MAX_TREE_HT * sizeof(char)); while ((ch = fgetc(fpIn)) != EOF) { for (i = 0; i < size; ++i) { if (huffmanCode[i].data == ch) { len = strlen(huffmanCode[i].code); for (j = 0; j < len; ++j) code[j] = huffmanCode[i].code[j]; fwrite(code, sizeof(char), len, fpOut); break; } } } fclose(fpIn); fclose(fpOut); printf("压缩成功!\n"); } // 解压文件 void decompressFile(char *fileName) { FILE *fpIn, *fpOut; struct HuffmanCode huffmanCode[256]; char ch, *code; int i, j, len, size; fpIn = fopen(fileName, "rb"); if (fpIn == NULL) { printf("打开文件失败!\n"); return; } fpOut = fopen("decompressed.txt", "w"); fread(&size, sizeof(int), 1, fpIn); for (i = 0; i < size; ++i) { fread(&huffmanCode[i].data, sizeof(char), 1, fpIn); fread(&len, sizeof(int), 1, fpIn); huffmanCode[i].code = (char*)malloc((len + 1) * sizeof(char)); fread(huffmanCode[i].code, sizeof(char), len, fpIn); huffmanCode[i].code[len] = '\0'; } code = (char*)malloc(MAX_TREE_HT * sizeof(char)); struct MinHeapNode *root = buildHuffmanTree(NULL, NULL, 0); struct MinHeapNode *curr = root; while ((ch = fgetc(fpIn)) != EOF) { for (i = 0; i < 8; ++i) { if (ch & (1 << (7 - i))) // ASCII码从高位开始读 curr = curr->right; else curr = curr->left; if (isLeaf(curr)) { fputc(curr->data, fpOut); curr = root; } } } fclose(fpIn); fclose(fpOut); printf("解压成功!\n"); } int main() { char data[] = {'a', 'b', 'c', 'd', 'e', 'f'}; int freq[] = {5, 9, 12, 13, 16, 45}; int size = sizeof(data) / sizeof(data[0]); struct HuffmanCode huffmanCode[256]; struct MinHeapNode* root = buildHuffmanTree(data, freq, size); char code[MAX_TREE_HT]; int top = 0; generateCodes(root, code, top, huffmanCode); printHuffmanCodes(huffmanCode, size); compressFile("input.txt", huffmanCode, size); decompressFile("compressed.dat"); return 0; } ``` 上述代码中,我们使用了最小优先队列来构建Huffman树。在生成Huffman编码时,我们使用了递归的方法遍历树,并记录下每个叶子节点的编码。在压缩文件时,我们将字符的Huffman编码写入输出文件中。在解压文件时,我们读取压缩文件中的编码信息和字符频率表,根据它们构建Huffman树,并根据编码信息还原原始字符串。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值