diff --git a/Compression/HuffmanArray.js b/Compression/HuffmanArray.js new file mode 100644 index 0000000000..88ca04ee5e --- /dev/null +++ b/Compression/HuffmanArray.js @@ -0,0 +1,136 @@ +/** + * Huffman Coding is a lossless data compression algorithm that uses variable-length codes to represent characters. + * + * The algorithm works by assigning shorter codes to characters that occur more frequently. This results in a compressed representation of the data. + * + * Huffman Coding is widely used in a variety of applications, including file compression, data transmission, and image processing. + * + * More information on Huffman Coding can be found here: https://en.wikipedia.org/wiki/Huffman_coding + */ + +/** + * Builds a frequency table from a string. + * @example + * buildFrequencyTable('this is an example for huffman encoding') + * returns { ' ': 6, a: 2, c: 1, d: 1, e: 4, f: 3, g: 1, h: 2, i: 3, l: 1, m: 1, n: 4, o: 1, p: 1, r: 1, s: 2, t: 2, u: 1, x: 1 } + * @param {string} data - The string to build the frequency table from. + * @returns {Object} - The frequency table. + */ +function buildFrequencyTable(data) { + const freqTable = {} + + for (const char of data) { + freqTable[char] = (freqTable[char] || 0) + 1 + } + + return freqTable +} + +/** + * A Huffman Node is a node in a Huffman tree. + * @class HuffmanNode + * @property {string} char - The character represented by the node. + * @property {number} freq - The frequency of the character. + * @property {HuffmanNode} left - The left child of the node. + * @property {HuffmanNode} right - The right child of the node. + */ +class HuffmanNode { + constructor(char, freq) { + this.char = char + this.freq = freq + this.left = null + this.right = null + } +} + +/** + * Builds a Huffman tree from a frequency table. + * @param {Object} freqTable - The frequency table to use for building the tree. + * @returns {HuffmanNode} - The root node of the Huffman tree. + */ +function buildHuffmanTree(freqTable) { + const nodes = Object.keys(freqTable).map( + (char) => new HuffmanNode(char, freqTable[char]) + ) + + while (nodes.length > 1) { + nodes.sort((a, b) => b.freq - a.freq) + const right = nodes.pop() + const left = nodes.pop() + const parent = new HuffmanNode(null, left.freq + right.freq) + parent.left = left + parent.right = right + nodes.push(parent) + } + + return nodes[0] +} + +/** + * Builds a Huffman code table from a Huffman tree. + * @param {HuffmanNode} root - The root node of the Huffman tree. + * @param {string} [prefix=''] - The prefix to use for the Huffman codes. + * @param {Object} [codes={}] - The Huffman code table. + * @returns {Object} - The Huffman code table. + */ +function buildHuffmanCodes(root, prefix = '', codes = {}) { + if (root) { + if (root.char) { + codes[root.char] = prefix + } + buildHuffmanCodes(root.left, prefix + '0', codes) + buildHuffmanCodes(root.right, prefix + '1', codes) + } + return codes +} + +/** + * Encodes a string using Huffman Coding. + * @param {string} data - The string to encode. + * @param {Object} freqTable - The frequency table to use for encoding. + * @returns {string} - The encoded string. + */ +function encodeHuffman(data, freqTable) { + const root = buildHuffmanTree(freqTable) + const codes = buildHuffmanCodes(root) + + let encodedData = '' + for (let char of data) { + encodedData += codes[char] + } + + return encodedData +} + +/** + * Decodes a string using Huffman Coding. + * @param {string} encodedData - The string to decode. + * @param {HuffmanNode} root - The root node of the Huffman tree. + * @returns {string} - The decoded string. + */ +function decodeHuffman(encodedData, root) { + let decodedData = '' + let currentNode = root + for (let bit of encodedData) { + if (bit === '0') { + currentNode = currentNode.left + } else { + currentNode = currentNode.right + } + + if (currentNode.char) { + decodedData += currentNode.char + currentNode = root + } + } + + return decodedData +} + +export { + buildHuffmanCodes, + buildHuffmanTree, + encodeHuffman, + decodeHuffman, + buildFrequencyTable +} diff --git a/Compression/HuffmanHeap.js b/Compression/HuffmanHeap.js new file mode 100644 index 0000000000..e314c57d6f --- /dev/null +++ b/Compression/HuffmanHeap.js @@ -0,0 +1,97 @@ +import { + BinaryHeap, + minHeapComparator +} from '../Data-Structures/Heap/BinaryHeap' +class Node { + constructor(symbol, frequency, left = null, right = null) { + this.symbol = symbol + this.frequency = frequency + this.left = left + this.right = right + } +} + +class HuffmanCoder { + constructor(data) { + this.data = data + this.codes = {} + this.buildHuffmanTree() + this.generateCodes(this.huffmanTree, '') + } + + buildFrequencyTable() { + const frequencyTable = {} + for (const char of this.data) { + if (char in frequencyTable) { + frequencyTable[char]++ + } else { + frequencyTable[char] = 1 + } + } + return frequencyTable + } + + buildHuffmanTree() { + const frequencyTable = this.buildFrequencyTable() + const minHeap = new BinaryHeap(minHeapComparator) + + for (const symbol in frequencyTable) { + minHeap.insert(new Node(symbol, frequencyTable[symbol])) + } + + while (minHeap.size() > 1) { + const left = minHeap.extractTop() + const right = minHeap.extractTop() + const combined = new Node( + null, + left.frequency + right.frequency, + left, + right + ) + minHeap.insert(combined) + } + + this.huffmanTree = minHeap.extractTop() + } + + generateCodes(node, code) { + if (!node) return + + if (node.symbol) { + this.codes[node.symbol] = code + } else { + this.generateCodes(node.left, code + '0') + this.generateCodes(node.right, code + '1') + } + } + + encode(data) { + let encodedString = '' + for (const char of data) { + encodedString += this.codes[char] + } + return encodedString + } + + decode(encodedString) { + let decodedString = '' + let currentNode = this.huffmanTree + + for (const bit of encodedString) { + if (bit === '0') { + currentNode = currentNode.left + } else { + currentNode = currentNode.right + } + + if (currentNode.symbol) { + decodedString += currentNode.symbol + currentNode = this.huffmanTree + } + } + + return decodedString + } +} + +export { HuffmanCoder } diff --git a/Compression/test/HuffmanArray.test.js b/Compression/test/HuffmanArray.test.js new file mode 100644 index 0000000000..bc79210c3c --- /dev/null +++ b/Compression/test/HuffmanArray.test.js @@ -0,0 +1,33 @@ +import { + buildHuffmanCodes, + buildHuffmanTree, + encodeHuffman, + decodeHuffman, + buildFrequencyTable +} from '../HuffmanArray' + +describe('Huffman Coding', () => { + let data, freqTable, root + + beforeEach(() => { + data = 'this is an example for huffman encoding' + freqTable = buildFrequencyTable(data) + root = buildHuffmanTree(freqTable) + }) + + it('should encode and decode a string correctly', () => { + const encodedData = encodeHuffman(data, freqTable) + const decodedData = decodeHuffman(encodedData, root) + + expect(decodedData).toEqual(data) + }) + + it('should build Huffman codes correctly', () => { + const codes = buildHuffmanCodes(root) + + expect(codes['t']).toEqual('01101') + expect(codes['h']).toEqual('0111') + expect(codes['i']).toEqual('0100') + expect(codes['s']).toEqual('1010') + }) +}) diff --git a/Compression/test/HuffmanHeap.test.js b/Compression/test/HuffmanHeap.test.js new file mode 100644 index 0000000000..870c217476 --- /dev/null +++ b/Compression/test/HuffmanHeap.test.js @@ -0,0 +1,19 @@ +import { HuffmanCoder } from '../HuffmanHeap' + +describe('HuffmanCoder', () => { + it('should encode and decode a simple string', () => { + const data = 'hello world' + const coder = new HuffmanCoder(data) + const encodedString = coder.encode(data) + const decodedString = coder.decode(encodedString) + expect(decodedString).toEqual(data) + }) + + it('should encode and decode a string with repeating characters', () => { + const data = 'aaaaabbbbcccdeeeee' + const coder = new HuffmanCoder(data) + const encodedString = coder.encode(data) + const decodedString = coder.decode(encodedString) + expect(decodedString).toEqual(data) + }) +}) diff --git a/Data-Structures/Heap/test/BinaryHeap.test.js b/Data-Structures/Heap/test/BinaryHeap.test.js index 56aef11e02..aef538d9fa 100644 --- a/Data-Structures/Heap/test/BinaryHeap.test.js +++ b/Data-Structures/Heap/test/BinaryHeap.test.js @@ -36,7 +36,7 @@ describe('BinaryHeap', () => { it('should handle insertion of duplicate values', () => { // Check if the heap handles duplicate values correctly minHeap.insert(2) - console.log(minHeap.heap); + console.log(minHeap.heap) expect(minHeap.heap).toEqual([1, 3, 2, 4, 8, 6, 2]) }) diff --git a/Data-Structures/Stack/EvaluateExpression.js b/Data-Structures/Stack/EvaluateExpression.js index e59a4a37c0..f8e976e16e 100644 --- a/Data-Structures/Stack/EvaluateExpression.js +++ b/Data-Structures/Stack/EvaluateExpression.js @@ -6,53 +6,53 @@ * @returns {number|null} - Result of the expression evaluation, or null if the expression is invalid. */ function evaluatePostfixExpression(expression) { - const stack = []; + const stack = [] // Helper function to perform an operation and push the result to the stack. Returns success. function performOperation(operator) { - const rightOp = stack.pop(); // Right operand is the top of the stack - const leftOp = stack.pop(); // Left operand is the next item on the stack + const rightOp = stack.pop() // Right operand is the top of the stack + const leftOp = stack.pop() // Left operand is the next item on the stack if (leftOp === undefined || rightOp === undefined) { - return false; // Invalid expression + return false // Invalid expression } switch (operator) { case '+': - stack.push(leftOp + rightOp); - break; + stack.push(leftOp + rightOp) + break case '-': - stack.push(leftOp - rightOp); - break; + stack.push(leftOp - rightOp) + break case '*': - stack.push(leftOp * rightOp); - break; + stack.push(leftOp * rightOp) + break case '/': if (rightOp === 0) { - return false; + return false } - stack.push(leftOp / rightOp); - break; + stack.push(leftOp / rightOp) + break default: - return false; // Unknown operator + return false // Unknown operator } - return true; + return true } - const tokens = expression.split(/\s+/); + const tokens = expression.split(/\s+/) for (const token of tokens) { if (!isNaN(parseFloat(token))) { // If the token is a number, push it to the stack - stack.push(parseFloat(token)); + stack.push(parseFloat(token)) } else { // If the token is an operator, perform the operation if (!performOperation(token)) { - return null; // Invalid expression + return null // Invalid expression } } } - return (stack.length === 1) ? stack[0] : null; + return stack.length === 1 ? stack[0] : null } -export { evaluatePostfixExpression }; +export { evaluatePostfixExpression } diff --git a/Data-Structures/Stack/test/EvaluateExpression.test.js b/Data-Structures/Stack/test/EvaluateExpression.test.js index eea764cac2..69a2e16365 100644 --- a/Data-Structures/Stack/test/EvaluateExpression.test.js +++ b/Data-Structures/Stack/test/EvaluateExpression.test.js @@ -1,22 +1,21 @@ -import { evaluatePostfixExpression } from '../EvaluateExpression.js'; +import { evaluatePostfixExpression } from '../EvaluateExpression.js' describe('evaluatePostfixExpression', () => { it('should evaluate a valid expression', () => { - const expression = '3 4 * 2 / 5 +'; // (3 * 4) / 2 + 5 = 11 - const result = evaluatePostfixExpression(expression); - expect(result).toBe(11); - }); + const expression = '3 4 * 2 / 5 +' // (3 * 4) / 2 + 5 = 11 + const result = evaluatePostfixExpression(expression) + expect(result).toBe(11) + }) it('should handle division by zero', () => { - const expression = '3 0 /'; // Division by zero - const result = evaluatePostfixExpression(expression); - expect(result).toBe(null); - }); + const expression = '3 0 /' // Division by zero + const result = evaluatePostfixExpression(expression) + expect(result).toBe(null) + }) it('should handle an invalid expression', () => { - const expression = '3 * 4 2 / +'; // Invalid expression - const result = evaluatePostfixExpression(expression); - expect(result).toBe(null); - }); - -}); + const expression = '3 * 4 2 / +' // Invalid expression + const result = evaluatePostfixExpression(expression) + expect(result).toBe(null) + }) +}) diff --git a/Maths/test/Determinant.test.js b/Maths/test/Determinant.test.js index df9d34df83..f6cba8241c 100644 --- a/Maths/test/Determinant.test.js +++ b/Maths/test/Determinant.test.js @@ -54,10 +54,7 @@ describe('Determinant', () => { 'Square matrix is required.' ], [[1, 3, 2, [5, 8, 6], 3], 'Input is not a valid 2D matrix.'] - ])( - 'Should return the error message.', - (matrix, expected) => { - expect(() => determinant(matrix)).toThrowError(expected) - } - ) + ])('Should return the error message.', (matrix, expected) => { + expect(() => determinant(matrix)).toThrowError(expected) + }) })
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: