zoukankan      html  css  js  c++  java
  • Huffman

    huffman是非常基础的压缩算法。

    实现霍夫曼树的方式有很多种,可以使用优先队列(Priority Queue)简单达成这个过程,给与权重较低的符号较高的优先级(Priority),算法如下:

    ⒈把n个终端节点加入优先队列,则n个节点都有一个优先权Pi,1 ≤ i ≤ n
    ⒉如果队列内的节点数>1,则:

    ⑴从队列中移除两个最大的Pi节点,即连续做两次remove(max(Pi), Priority_Queue)
    ⑵产生一个新节点,此节点为(1)之移除节点之父节点,而此节点的权重值为(1)两节点之权重和
    ⑶把(2)产生之节点加入优先队列中
    ⒊最后在优先队列里的点为树的根节点(root)

    而此算法的时间复杂度( Time Complexity)为O(n log n);因为有n个终端节点,所以树总共有2n-1个节点,使用优先队列每个循环须O(log n)。
    此外,有一个更快的方式使时间复杂度降至线性时间(Linear Time)O(n),就是使用两个队列(Queue)创件霍夫曼树。第一个队列用来存储n个符号(即n个终端节点)的权重,第二个队列用来存储两两权重的合(即非终端节点)。此法可保证第二个队列的前端(Front)权重永远都是最小值,且方法如下:

    ⒈把n个终端节点加入第一个队列(依照权重大小排列,最小在前端)
    ⒉如果队列内的节点数>1,则:

    ⑴从队列前端移除两个最低权重的节点
    ⑵将(1)中移除的两个节点权重相加合成一个新节点
    ⑶加入第二个队列
    ⒊最后在第一个队列的节点为根节点

    虽然使用此方法比使用优先队列的时间复杂度还低,但是注意此法的第1项,节点必须依照权重大小加入队列中,如果节点加入顺序不按大小,则需要经过排序,则至少花了O(n log n)的时间复杂度计算。
    但是在不同的状况考量下,时间复杂度并非是最重要的,如果我们今天考虑英文字母的出现频率,变量n就是英文字母的26个字母,则使用哪一种算法时间复杂度都不会影响很大,因为n不是一笔庞大的数字。

      1 #include <iostream>
      2 #include <algorithm>
      3 #include <unordered_map>
      4 #include <vector>
      5 #include <queue>
      6 #include <fstream>
      7 #include <sstream>
      8 #include <string>
      9 
     10 using namespace std;
     11 
     12 class Huffman {
     13     public:
     14         Huffman() {}
     15         ~Huffman() {
     16             freeTree(root);
     17         }
     18 
     19         void init(string filename) {
     20             ifstream in(filename.c_str());
     21             string line; 
     22             while(getline(in, line)) {
     23                 stringstream ss(line);
     24                 char symbol; 
     25                 float p;
     26                 ss >> symbol >> p;
     27                 symbolInfo.push_back(new Node(symbol, p));
     28             }
     29             root = buildTree2();
     30             generateCodes(root, "");
     31         }
     32 
     33         void print() const {
     34             for (auto it = codes.begin(); it != codes.end(); ++it) {
     35                 cout << it->first << ": " << it->second << endl;
     36             }
     37         }
     38 
     39         string encode(string input) {
     40             stringstream ans;
     41             for (int i = 0; i < input.length(); ++i) {
     42                 ans << codes[input[i]];
     43             }
     44             return ans.str();
     45         }
     46 
     47         string decode(string input) {
     48             if (root == NULL) return "";
     49             stringstream ans;
     50             for (int i = 0; i < input.length(); ) {
     51                 Node* p = root;
     52                 for ( ; p != NULL; ++i) {
     53                     if (p->left == NULL && p->right == NULL) {
     54                         ans << p->symbol;
     55                         break;
     56                     }
     57                     if (input[i] == '0') {
     58                         p = p->left;
     59                     } else if (input[i] == '1') {
     60                         p = p->right;
     61                     } else {
     62                         return "";
     63                     }
     64                 }
     65             }
     66             return ans.str();
     67         }
     68     private:
     69         struct Node {
     70             char symbol;
     71             float p;
     72             Node* left;
     73             Node* right;
     74             Node(char s, float p, Node* l = NULL, Node* r = NULL):symbol(s), p(p), left(l), right(r) {}
     75         };
     76         
     77         static bool nodeCompare(Node* n1, Node* n2) {
     78             return n1->p > n2->p;
     79         }
     80     
     81         // O(nlgn)
     82         Node* buildTree() {
     83             if (symbolInfo.empty()) return NULL;
     84             make_heap(symbolInfo.begin(), symbolInfo.end(), nodeCompare);
     85             while (symbolInfo.size() > 1) {
     86                 // get the smallest
     87                 Node* n1 = symbolInfo.front();
     88                 pop_heap(symbolInfo.begin(), symbolInfo.end(), nodeCompare);
     89                 symbolInfo.pop_back();
     90                 // get the second smallest
     91                 Node* n2 = symbolInfo.front();
     92                 pop_heap(symbolInfo.begin(), symbolInfo.end(), nodeCompare);
     93                 symbolInfo.pop_back();
     94                 
     95                 Node* n3 = new Node('@', n1->p + n2->p, n1, n2);
     96                 symbolInfo.push_back(n3);
     97                 push_heap(symbolInfo.begin(), symbolInfo.end(), nodeCompare);
     98             }
     99             return symbolInfo[0];
    100         }
    101 
    102         class Comparator {
    103             public:
    104             bool operator() (const Node* n1, const Node* n2) const {
    105                 return n1->p > n2->p;
    106             }
    107         };
    108 
    109         Node* buildTree2() {
    110             if (symbolInfo.empty()) return NULL;
    111             priority_queue<Node*, vector<Node*>, Comparator> queue(symbolInfo.begin(), symbolInfo.end());
    112             while (queue.size() > 1) {
    113                 Node* n1 = queue.top(); 
    114                 queue.pop();
    115                 Node* n2 = queue.top();
    116                 queue.pop();
    117                 Node* n3 = new Node('@', n1->p + n2->p, n1, n2);
    118                 queue.push(n3);    
    119             }
    120             return queue.top();
    121         }
    122     
    123         void freeTree(Node* p) {
    124             if (p == NULL) return;
    125             freeTree(p->left);
    126             freeTree(p->right);
    127             delete p;
    128             p = NULL;
    129         }
    130 
    131         void generateCodes(Node* p, string str) {
    132             if (p == NULL) return;
    133             if (p->left == NULL && p->right == NULL) {
    134                 codes[p->symbol] = str;
    135             }
    136             
    137             generateCodes(p->left, str + "0");
    138             generateCodes(p->right, str + "1");
    139         }
    140 
    141         vector<Node*> symbolInfo;
    142         unordered_map<char, string> codes;
    143         Node* root;
    144 };
    145 
    146 int main() {
    147     Huffman huffman;
    148     huffman.init("input.txt");
    149     huffman.print();
    150 
    151     string str = "abcdabcdab";
    152     string encode = huffman.encode(str);
    153     cout << str << endl << encode << endl;
    154     cout << huffman.decode(encode) << endl;
    155     return 0;
    156 }

    这里用了stl的make_heap之类的函数,也尝试用priority_queue。但是两指重构的比较器的形式不同。注意priority_queue的比较器是作为模板参数传进去的,而且是定义成类。

    可以用两个简单队列实现O(n)的算法,前提是一开始频率已经排好序。用vector是可以模拟queue,但是pop_front()的效率比较低。

     huffman的正确性证明可以看这篇:http://mindhacks.cn/2011/07/10/the-importance-of-knowing-why-part3/,讲得相当清晰了。

  • 相关阅读:
    DOS命令收集
    iis6配置支持.net4.0
    正则表达式限制文本框
    剖析XML(第一讲)
    DataTime.ToString("xx") 转换
    .net面试题大汇集
    django学习笔记(一)
    django学习笔记(一)
    django学习笔记(二)
    django学习笔记(二)
  • 原文地址:https://www.cnblogs.com/linyx/p/3986437.html
Copyright © 2011-2022 走看看