Problem3.cpp (4765B)
1 // Problem 3: Huffman Coding 2 // Description: Implement a Huffman coding algorithm. 3 // Course: IT405G - Datastructures and Algorithms 4 // Authors: William Lindholm, Lili Tran, Victor Adamson 5 // Date: 29-11-2023 6 // 7 8 9 #include <iostream> 10 #include <vector> 11 #include <queue> 12 #include <map> 13 14 using namespace std; 15 16 class Tree 17 { 18 public: 19 /* 20 * Constructor for leaf node 21 * @param w: the weight of the node 22 * @param c: the character of the node 23 */ 24 Tree(int w, char c) 25 { 26 this->weight = w; 27 this->c = c; 28 } 29 30 /* 31 * Constructor for internal node 32 * @param w: the weight of the node 33 * @param t1: the left subtree 34 * @param t2: the right subtree 35 */ 36 Tree(int w, Tree* t1, Tree* t2) 37 { 38 this->weight = w; 39 this->left = t1; 40 this->right = t2; 41 } 42 43 /* 44 * Destructor 45 */ 46 ~Tree() 47 { 48 delete(left); 49 delete(right); 50 } 51 52 /* 53 * Get the weight of the node 54 * @return: the weight of the node 55 */ 56 int getWeight() const 57 { 58 return this->weight; 59 } 60 61 /* 62 * Print the tree 63 * Example of output: 64 * 0 : a 65 * 1 0 : c 66 * 1 1 : b 67 * @param bitString: the bitstring of the node 68 */ 69 void printTree(const string& bitString = "") const 70 { 71 if (!left && !right) { 72 cout << bitString << ": " << c << endl; 73 return; 74 } 75 76 if (left) left->printTree(bitString + "0"); 77 if (right) right->printTree(bitString + "1"); 78 } 79 80 /* 81 * Construct a map of the characters and their codes 82 * Since codes is passed as a reference, it will be modified 83 * @param codes: the map to construct 84 * @param bitString: the bitstring of the node 85 */ 86 void constructMap(map<char, string>& codes, const string& bitString = "") 87 { 88 if (!left && !right) { 89 codes[c] = bitString; 90 return; 91 } 92 93 if (left) left->constructMap(codes, bitString + "0"); 94 if (right) right->constructMap(codes, bitString + "1"); 95 } 96 97 private: 98 Tree* left; 99 Tree* right; 100 int weight; 101 char c; 102 }; 103 104 struct TreeWrapper 105 { 106 TreeWrapper() 107 { 108 tree = NULL; 109 } 110 111 TreeWrapper(Tree* t) 112 { 113 tree = t; 114 } 115 116 bool operator<(const TreeWrapper& tw) const 117 { 118 return tree->getWeight() > tw.tree->getWeight(); 119 } 120 121 Tree* tree; 122 }; 123 124 125 class HuffmanEncoder 126 { 127 public: 128 /* 129 * Constructor 130 * @param plainText: the string to encode 131 */ 132 HuffmanEncoder(string plainText) 133 { 134 this->plainText = plainText; 135 } 136 137 /* 138 * Encode the string 139 * @return: the encoded string 140 */ 141 void printCodes() 142 { 143 auto subTrees = createLeaves(); 144 this->huffmanTree = buildTree(subTrees); 145 Tree* root = this->getRoot(); 146 root->printTree(); 147 } 148 149 150 /* 151 * Get the codes of the characters 152 * @return: a map of the characters and their codes 153 */ 154 map<char, string> getCodes() 155 { 156 auto subTrees = createLeaves(); 157 this->huffmanTree = buildTree(subTrees); 158 Tree* root = this->getRoot(); 159 map<char, string> codes; 160 root->constructMap(codes); 161 return codes; 162 } 163 164 /* 165 * Encode the string 166 * @return: the encoded string 167 */ 168 string encode() 169 { 170 auto codes = getCodes(); 171 string encodedString = ""; 172 for (char c : plainText) 173 { 174 encodedString += codes[c]; 175 encodedString += " "; 176 } 177 return encodedString; 178 } 179 180 /* 181 * Get the root of the tree 182 * @return: the root of the 183 */ 184 Tree* getRoot() 185 { 186 return huffmanTree.top().tree; 187 } 188 189 private: 190 string plainText; 191 priority_queue<TreeWrapper> huffmanTree; 192 193 /* 194 * Create the leaves of the tree, and push them to a priority queue 195 * Note: the priority queue is sorted by the weight of the nodes 196 * But the tree is not built yet 197 * @return: a priority queue of the leaves 198 */ 199 priority_queue<TreeWrapper> createLeaves() 200 { 201 priority_queue<TreeWrapper> q; 202 203 map<char, int> charWeights; 204 205 // calculate frequencies 206 for (char c : plainText) 207 { 208 charWeights[c]++; 209 } 210 211 // Create leaves and push them to the queue 212 for (auto& pair : charWeights) 213 { 214 q.push(TreeWrapper(new Tree(pair.second, pair.first))); 215 } 216 217 return q; 218 } 219 220 /* 221 * Build the tree from the priority queue 222 * @param q: the priority queue of the leaves 223 * @return: the root of the tree 224 */ 225 priority_queue<TreeWrapper> buildTree(priority_queue<TreeWrapper> q) 226 { 227 if (q.size() == 1) 228 { 229 return q; 230 } 231 232 TreeWrapper t1 = q.top(); 233 q.pop(); 234 TreeWrapper t2 = q.top(); 235 q.pop(); 236 q.push(TreeWrapper(new Tree(t1.tree->getWeight() + t2.tree->getWeight(), t1.tree, t2.tree))); 237 238 return buildTree(q); 239 } 240 }; 241 242 243 int main() 244 { 245 string unEncodedString = "AAAABBBCCCCCCCCCCCCCCCCCCCCCCCCCCCD"; 246 HuffmanEncoder huffmanTree(unEncodedString); 247 huffmanTree.printCodes(); 248 249 string encodedString = huffmanTree.encode(); 250 printf("Encoded string: %s\n", encodedString.c_str()); 251 252 int unEncodedLen = (int)(unEncodedString.length()*8); 253 int encodedLen = (int)encodedString.length(); 254 255 printf("Unencoded length: %d\n", unEncodedLen); 256 printf("Encoded length: %d\n", encodedLen); 257 printf("saved %d bits\n", unEncodedLen - encodedLen); 258 259 return 0; 260 };