Nadesłany przez Jan Wojciechowski, 24 marca 2012 18:39
Kod przedstawiony poniżej przedstawia główną część rozwiązania problemu.Pobierz pełne rozwiązanie.
Jeżeli nie odpowiada Ci sposób formatowania kodu przez autora skorzystaj z pretty printer'a i dostosuj go automatycznie do siebie.
huffman.cpp:
//Kody Huffmana //Jan Wojceichowski //www.algorytm.org #include <iostream> #include <fstream> #include <vector> #include <algorithm> const bool bLeft = false; const bool bRight = true; typedef unsigned char Byte; Byte setBit(Byte x, unsigned int index) { return x | (1 << index); } bool getBit(Byte x, unsigned int index) { return (x & (1 << index)) != 0; } class Node { public: static bool greaterThan(Node *a, Node *b) { if(a != NULL && b != NULL) { return a->count > b->count; } else if(a != NULL) { return true; } else { return false; } } static bool lessThan(Node *a, Node *b) { if(a != NULL && b != NULL) { return a->count < b->count; } else if(b != NULL) { return true; } else { return false; } } static void removeTree(Node *root) { if(root == NULL) { return; } removeTree(root->left); removeTree(root->right); delete root; } Node(Byte b, Node *l = NULL, Node *r = NULL, unsigned long c = 0) : left(l), right(r), count(c), byte(b) { } bool isLeaf() const { return this->left == NULL && this->right == NULL; } Node *left, *right; unsigned long count; Byte byte; }; // Wyzncza kod dla każdego liścia w danym drzewie. void mapTree(Node *root, std::vector<bool> *codes, std::vector<bool> &prefix = std::vector<bool>()) { if(root == NULL) { return; } if(root->left == NULL && root->right == NULL) { // Jesteśmy w liściu. Znaleźliśmy kod jednego bajtu. codes[root->byte] = prefix; } if(root->left != NULL) { prefix.push_back(bLeft); mapTree(root->left, codes, prefix); prefix.pop_back(); } if(root->right != NULL) { prefix.push_back(bRight); mapTree(root->right, codes, prefix); prefix.pop_back(); } } // Zapisuje drzewo do strumienia danych. void saveTree(Node *root, std::ostream &output, Byte &accumulator, unsigned int &bitIndex) { if(root == NULL) { return; } if(bitIndex == 8) { output.write(reinterpret_cast<char *>(&accumulator), sizeof(accumulator)); accumulator = 0; bitIndex = 0; } if(root->isLeaf()) { accumulator = setBit(accumulator, bitIndex); ++bitIndex; if(bitIndex == 8) { output.write(reinterpret_cast<char *>(&accumulator), sizeof(accumulator)); accumulator = 0; bitIndex = 0; } for(unsigned int i = 0; i < 8; ++i) { if(getBit(root->byte, i)) { accumulator = setBit(accumulator, bitIndex); } ++bitIndex; if(bitIndex == 8) { output.write(reinterpret_cast<char *>(&accumulator), sizeof(accumulator)); accumulator = 0; bitIndex = 0; } } } else { ++bitIndex; if(bitIndex == 8) { output.write(reinterpret_cast<char *>(&accumulator), sizeof(accumulator)); accumulator = 0; bitIndex = 0; } saveTree(root->left, output, accumulator, bitIndex); saveTree(root->right, output, accumulator, bitIndex); } } // Wczytuje drzewo ze strumienia danych. bool loadTree(std::istream &input, Byte &accumulator, unsigned int &bitIndex, Node *&root) { if(bitIndex == 8) { if(!input.read(reinterpret_cast<char *>(&accumulator), sizeof(accumulator))) { return false; } bitIndex = 0; } root = new Node(0); bool bit = getBit(accumulator, bitIndex); ++bitIndex; if(bit) { for(unsigned int i = 0; i < 8; ++i) { if(bitIndex == 8) { if(!input.read(reinterpret_cast<char *>(&accumulator), sizeof(accumulator))) { delete root; root = NULL; return false; } bitIndex = 0; } if(getBit(accumulator, bitIndex)) { root->byte = setBit(root->byte, i); } ++bitIndex; } } else { if(!loadTree(input, accumulator, bitIndex, root->left)) { delete root; root = NULL; return false; } if(!loadTree(input, accumulator, bitIndex, root->right)) { Node::removeTree(root); root = NULL; return false; } } return true; } void compress(std::istream &input, std::ostream &output) { Byte buffer; std::istream::pos_type start = input.tellg(); std::vector<Node *> nodes(256); for(unsigned int i = 0; i < 256; ++i) { nodes[i] = new Node(i); } // Przechodzimy po strumieniu wejściowym, żeby policzyć liczbę wystąpienia każdego bajtu. while(input.read(reinterpret_cast<char *>(&buffer), sizeof(buffer))) { ++(nodes[buffer]->count); } // Tworzymy drzewo. std::sort(nodes.begin(), nodes.end(), Node::greaterThan); while(nodes.size() > 1) { Node *a, *b, *c; a = nodes.back(); nodes.pop_back(); b = nodes.back(); nodes.pop_back(); c = new Node(0, a, b, a->count + b->count); nodes.insert(std::upper_bound(nodes.begin(), nodes.end(), c, Node::greaterThan), c); } Node *root = nodes.back(); nodes.clear(); Byte accumulator = 0; // Akumulator bitów. unsigned int bitIndex = 0; // Licznik bitów. saveTree(root, output, accumulator, bitIndex); // Zapisujemy drzewo. // Dla usprawnienia dalszych operacji zapisujemy kody w tablicy. std::vector<bool> codes[256]; mapTree(root, codes); Node::removeTree(root); // Drzewo już nie jest potrzebne. // Wracamy na początek strumienia danych. input.clear(); input.seekg(start); while(input.read(reinterpret_cast<char *>(&buffer), sizeof(buffer))) { for(std::vector<bool>::const_iterator i = codes[buffer].begin(); i != codes[buffer].end(); ++i) { if(*i) { accumulator = setBit(accumulator, bitIndex); } ++bitIndex; if(bitIndex == 8) { output.write(reinterpret_cast<char *>(&accumulator), sizeof(accumulator)); bitIndex = 0; accumulator = 0; } } } if(bitIndex > 0) { output.write(reinterpret_cast<char *>(&accumulator), sizeof(accumulator)); bitIndex = 0; accumulator = 0; } } bool decompress(std::istream &input, std::ostream &output) { Node *root = NULL; Byte accumulator = 0; unsigned int bitIndex = 8; // Wczytujemy drzewo. if(!loadTree(input, accumulator, bitIndex, root)) { return false; } Node *current = root; while(true) { // Sprawdzamy czy nie ma błędu. if(current == NULL) { Node::removeTree(root); // Sprzątanie. return false; } if(current->isLeaf()) { // Zapisujemy jeden bajt do wyniku. output.write(reinterpret_cast<char *>(&(current->byte)), sizeof(current->byte)); current = root; } // Odczytujemy kolejny bajt. if(bitIndex == 8) { if(!input.read(reinterpret_cast<char *>(&accumulator), sizeof(accumulator))) { break; } bitIndex = 0; } // Odczytujemy jeden bit. bool bit = getBit(accumulator, bitIndex); ++bitIndex; if(bit == bLeft) { current = current->left; } else { current = current->right; } } Node::removeTree(root); // Sprzątanie. return true; } int main() { std::ifstream input; std::ofstream output; std::cout << "Wybierz opcje:" << std::endl << "c - kompresuj plik \"plik.txt\"" << std::endl << "d - dekompresuj plik \"plik skompresowany\"" << std::endl << "<inny znak> - zakoncz program" << std::endl; char option; std::cin >> option; if(option == 'c') { input.open("plik.txt", std::ios::binary); if(!input.is_open()) { std::cout << "Nie udalo sie otworzyc pliku \"plik.txt\"."; ::getchar(); return 0; } output.open("plik skompresowany", std::ios::binary); if(!output.is_open()) { std::cout << "Nie udalo sie otworzyc pliku \"plik skompresowany\"."; input.close(); ::getchar(); return 0; } compress(input, output); output.close(); input.close(); } else if(option == 'd') { input.open("plik skompresowany", std::ios::binary); if(!input.is_open()) { std::cout << "Nie udalo sie otworzyc pliku \"plik skompresowany\"."; ::getchar(); return 0; } output.open("plik zdekompresowany.txt", std::ios::binary); if(!output.is_open()) { std::cout << "Nie udalo sie otworzyc pliku \"plik zdekompresowany.txt\"."; input.close(); ::getchar(); return 0; } if(!decompress(input, output)) { std::cout << "Nie udalo sie zdekompresowac pliku \"plik skompresowany\"." << std::endl; ::getchar(); } output.close(); input.close(); } return 0; }
Czy ja muszę się zastanawiać czy takie Node to nazwa zmiennej czy coś w języku C? Muszę?