DSA-Assignments

Log | Files | Refs | README

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 };