Advertisement
Gistrec

Кодирование Хаффмана

Apr 1st, 2018
248
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 12.10 KB | None | 0 0
  1. // Пример использования
  2. #include <string>
  3. #include <iostream>
  4.  
  5. #include "HuffmanTree.cpp"
  6.  
  7. using std::string;
  8. using std::cout;
  9. using std::endl;
  10.  
  11.      
  12. int main() {
  13.     string text = "KOL_OKOLO_KOLOKOLA";
  14.  
  15.     HuffmanTree* tree = new HuffmanTree(text);
  16.     string compress = tree->getCompressed();
  17.  
  18.     cout << compress << endl;
  19.     // Выведет 001101101110011011110100110111001101100
  20.     system("pause");
  21. }
  22.  
  23.  
  24. #include <vector>
  25. #include <map>
  26. #include <string>
  27.  
  28. using std::string;
  29. using std::vector;
  30. using std::pair;
  31. using std::map;
  32.  
  33. // Алгоритм:
  34. // 1. Получаем все символы строки и кол-во их вхождений
  35. // 2. Составляем поддеревья из всех символов
  36. // 3. Составляем дерево Хаффмана из всех поддеревьев
  37. // 4. Обходим дерево Хаффмана, создавая набор
  38. //    какой символ на что заменить
  39. class HuffmanTree {
  40. private:
  41.      // Узел дерева
  42.      // Хранит вес
  43.      // Хранит либо указатели на поддеревья (один или два)
  44.      // Либо символы (один или два)
  45.     struct Node {
  46.         int weight; // Вес вершины
  47.  
  48.         // Первый и второй символ
  49.         char first_symbol;
  50.         char second_symbol;
  51.  
  52.         // Указатели на левое и правое поддеревья
  53.         Node* left;
  54.         Node* right;
  55.  
  56.         // Конструктор создания поддерева
  57.         // Где будут хранится один или два символа
  58.         // Указатели на поддеревья, соответственно равны nullptr
  59.         Node(int weight, char first_symbol, char second_symbol = '\0')
  60.             : left(nullptr), right(nullptr) {
  61.             this->weight = weight;
  62.             this->first_symbol = first_symbol;
  63.             this->second_symbol = second_symbol;
  64.         }
  65.  
  66.         // Конструктор для создания поддерева
  67.         // Где будут хранится указатели на правое и левое поддерево
  68.         // Символы, соответственно будут равны \0
  69.         Node(int weight, Node* left, Node* right = nullptr)
  70.             : first_symbol('\0'), second_symbol('\0') {
  71.             this->weight = weight;
  72.             this->left = left;
  73.             this->right = right;
  74.         }
  75.  
  76.         // Функция возаращает true, если узел хранит только один символ
  77.         // Нужно при обходе дерева Хаффмана
  78.         bool isSingleLeaf() {
  79.             return (first_symbol != '\0' && second_symbol == '\0') ||
  80.                    (first_symbol == '\0' && second_symbol != '\0');
  81.         }
  82.  
  83.         // В деструкторе удаляем поддеревья
  84.         ~Node() {
  85.             if (left  != nullptr) delete left;
  86.             if (right != nullptr) delete right;
  87.         }
  88.     };
  89.  
  90.     // Увеличиваем кол-во вхождений символа symbol в векторе frequencie на единицу
  91.     // Для этого ищем нужный pair. Если такого не нашлось - создаем
  92.     void increaseCount(vector<pair<char, int>> &frequencie, char symbol) {
  93.         for (auto &pair : frequencie) {
  94.             // Если нашли
  95.             if (pair.first == symbol) {
  96.                 pair.second += 1;
  97.                 return;
  98.             }
  99.         }
  100.         // Если не нашли - добавляем {symbol, 1} в конец вектора
  101.         frequencie.push_back(pair<char, int>(symbol, 1));
  102.     }
  103.  
  104.     // Возвращает неотсортированный вектор из набора
  105.     // символов и кол-ва их вхождений в строку string
  106.     // Этот набор реализуется классом std::pair
  107.     //
  108.     // Из "AABBCA" получим {A, 3}, {B, 2}, {C, 1}
  109.     vector<pair<char, int>> getFrequencies(string const &string) {
  110.         vector<pair<char, int>> frequencie;
  111.         for (auto symbol : string) {
  112.             increaseCount(frequencie, symbol);
  113.         }
  114.         return frequencie;
  115.     }
  116.  
  117.     // Функция нужна для извлечения (возврата + удаления)
  118.     // набора {Символ => Кол-во вхождений}, с минимальным кол-вом вхождений
  119.     pair<char, int> executeMinimalPair(vector<pair<char, int>> &frequencie) {
  120.         pair<char, int> result = frequencie[0];
  121.         int result_position = 0; // Позиция минимального (возаращаемого) элемента
  122.         int i = 0;
  123.         for (auto &pair : frequencie) {
  124.             if (pair.second < result.second) {
  125.                 result = pair;
  126.                 result_position = i;
  127.             }
  128.             i += 1;
  129.         }
  130.         // Удаляем минимальный (возвращаемый) элемент
  131.         frequencie.erase(frequencie.begin() + result_position);
  132.         return result;
  133.     }
  134.  
  135.     // Функция нужна дли извлечения (возврата + удаления)
  136.     // поддерева с минимальным весом
  137.     Node* executeMinimalTree() {
  138.         Node* result = nodes[0];
  139.         int result_position = 0; // Позиция поддерева с минимальным весом
  140.         int position = 0;
  141.         for (auto &tree : nodes) {
  142.             if (tree->weight < result->weight) {
  143.                 result = tree;
  144.                 result_position = position;
  145.             }
  146.             position += 1;
  147.         }
  148.         // Удаляем поддерево с минимальным весом из списка вершин
  149.         nodes.erase(nodes.begin() + result_position);
  150.         return result;
  151.     }
  152.  
  153.     // Функция нужна для создания дерева Хаффмана из поддеревьев
  154.     // 1. Пока в nodes больше одной вершины - создается их родитель,
  155.     //    с весом, равным их суммарному весу
  156.     // 2. Родитель добавляется в список вершин,
  157.     //    а его два потомка удаляются из этого списка
  158.     // 3. Вызываем функцию, которая обойдет дерево Хаффмана
  159.     //    и для каждого символа создатс строку, на которую его нужно заменить
  160.     void createHuffmanTree() {
  161.         // Пока в nodes хранится не одна вершина - не корень дерева
  162.         Node* first;  // Первое поддерево
  163.         Node* second; // Второе поддерево
  164.         while (nodes.size() != 1) {
  165.             first  = executeMinimalTree();
  166.             second = executeMinimalTree();
  167.             Node* newNode = new Node(first->weight + second->weight, // Вес двух поддеревьев
  168.                                      first, second);                 // Два поддерева
  169.             nodes.push_back(newNode); // Добавляем родителя в список вершин
  170.         }
  171.         // На данном этапе у нас в nodes хранится одна
  172.         // вершина, которая является деревом Хаффмана
  173.         // Вызываем функцию, которая обойдет все вершины
  174.         // и для каждого символа присвоит свою замену
  175.         parseHuffmanTree(nodes[0], "");
  176.     }
  177.  
  178.     // Рекурсивная функция для обхода дерева Хаффмана
  179.     // 1. Если дерево - узел, то обходим левое  поддерево, добавив 0
  180.     //                           обходим правое поддерево, добавив 1
  181.     // 2. Если дерево - лист, то надо ли прибавлять 0 или 1 зависит от того
  182.     //                                  сколько символов содержит этот узел.
  183.     //    Если дерево содержит один символ - то 0 или 1 прибавлять не нужно
  184.     //    Если дерево содержит два символа - то нужно
  185.     void parseHuffmanTree(Node* node, string prefix) {
  186.         // Если дерево - лист. Т.е. содержит символ, но не содержит поддеревья
  187.         if (node->first_symbol != '\0') {
  188.             if (node->isSingleLeaf()) replace[node->first_symbol] = prefix;
  189.             else replace[node->first_symbol] = prefix + "0";
  190.         }
  191.         if (node->second_symbol != '\0') {
  192.             if (node->isSingleLeaf()) replace[node->second_symbol] = prefix;
  193.             else replace[node->second_symbol] = prefix + "1";
  194.         }
  195.         // Если дерево - узел. Т.е. не содержит символ, а содержит поддеревья
  196.         if (node->left != nullptr) {
  197.             parseHuffmanTree(node->left, prefix + "0");
  198.  
  199.         }
  200.         if (node->right != nullptr) {
  201.             parseHuffmanTree(node->right, prefix + "1");
  202.         }
  203.     }
  204.    
  205.     string text;
  206.     vector<Node*> nodes; // Хранится указатели на все поддеревья
  207.     map<char, string> replace; // Хранит набор из символов и строк, на которые их заменяем
  208.  
  209. public:
  210.     // Входные данныех: строка
  211.     // 1. Для каждого символа вычисляем, сколько раз он встречается в тексте
  212.     //    Получаем набор из значений {Cимвол => Кол-во вхождений}
  213.     //    Этот набор реализуется классом std::pair
  214.     // 2. Из этого набора получаем все поддеревья и помещаются в вектор nodes
  215.     //    Поддеревья получаем так:
  216.     //            I.   Выбираем два символа с наименьшими весами
  217.     //            II.  Создаем из них поддерево, с весом, равным их суммарному весу
  218.     //            III. Помешаем поддерево в в вектор nodes
  219.     //    Так же нужно обработать ситуацию, когда остался только один символ.
  220.     //    Тогда создаем поддерево только с одним символом и помещаем его в nodes
  221.     // 3. Вызываем функцию, в которой их всех поддеревьев соберем дерево Хаффмана
  222.     HuffmanTree(string text) {
  223.         this->text = text;
  224.         // Получаем набор из {Символ => Сколько раз он встречается в тексте}
  225.         vector<pair<char, int>> frequencie = getFrequencies(text);
  226.         pair<char, int> first;
  227.         pair<char, int> second;
  228.  
  229.         Node* newTree = nullptr;
  230.         // При создании поддерева у нас будет два случая
  231.         // 1. Когда кол-во оставшихся символов больше или равно двум
  232.         //    Тогда создается поддерево из двух символов
  233.         while (frequencie.size() > 1) {
  234.             first  = executeMinimalPair(frequencie);
  235.             second = executeMinimalPair(frequencie);
  236.             Node* newTree = new Node(first.second + second.second,   // Сумма весов
  237.                                      first.first, second.first);     // Два символа
  238.             nodes.push_back(newTree);
  239.  
  240.         }
  241.         // 2. Когда кол-во элементов равно единице
  242.         //    Тогда создаем подедерво с одним символом
  243.         if (frequencie.size() == 1) {
  244.             first = executeMinimalPair(frequencie);
  245.             Node* newTree = new Node(first.second, first.first); // Вес и и один символ
  246.             nodes.push_back(newTree);
  247.         }
  248.         // Вызываем функцию, в которой из всех
  249.         // поддеревьев создадим дерево Хаффмана
  250.         createHuffmanTree();
  251.     }
  252.  
  253.     // Пользовательская функция, возвращает
  254.     // набор из симвов и строк, на которые из заменяем
  255.     map<char, string> getReplacements() {
  256.         return replace;
  257.     }
  258.  
  259.     // Пользовательская функция, возвращает сжатую строку
  260.     string getCompressed() {
  261.         string result;
  262.         for (auto symbol : text) {
  263.             result += replace[symbol];
  264.         }
  265.         return result;
  266.     }
  267.  
  268.     // В деструкторе удаляем дерево Хаффмана
  269.     ~HuffmanTree() {
  270.         delete nodes[0];
  271.     }
  272. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement