Skip to content
Prev Previous commit
Next Next commit
Huffman Compression Algorithm Comments
  • Loading branch information
aladin002dz committed Oct 11, 2023
commit c4e98aa0232bb5461352a74f83bc2f85d2d5bda9
122 changes: 60 additions & 62 deletions Compression/Huffman.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

/**
* Huffman Coding is a lossless data compression algorithm that uses variable-length codes to represent characters.
*
Expand All @@ -18,16 +17,15 @@
* @returns {Object} - The frequency table.
*/
function buildFrequencyTable(data) {
const freqTable = {}
const freqTable = {}

for (const char of data) {
freqTable[char] = (freqTable[char] || 0) + 1
}
for (const char of data) {
freqTable[char] = (freqTable[char] || 0) + 1
}

return freqTable
return freqTable
}


/**
* A Huffman Node is a node in a Huffman tree.
* @class HuffmanNode
Expand All @@ -37,35 +35,35 @@ function buildFrequencyTable(data) {
* @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
}
constructor(char, freq) {
this.char = char
this.freq = freq
this.left = null
this.right = null
}
}

/**
* Builds a Huffman tree from a frequency table.
* 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) => a.freq - b.freq)
const left = nodes.shift()
const right = nodes.shift()
const parent = new HuffmanNode(null, left.freq + right.freq)
parent.left = left
parent.right = right
nodes.push(parent)
}

return nodes[0]
const nodes = Object.keys(freqTable).map(
(char) => new HuffmanNode(char, freqTable[char])
)

while (nodes.length > 1) {
nodes.sort((a, b) => a.freq - b.freq)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you sort in every iteration?

Copy link
Contributor Author

@aladin002dz aladin002dz Oct 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because when pushing a new node, the order may change.

Copy link
Collaborator

@appgurueu appgurueu Oct 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be more appropriate to use a max heap here (our existing implementations should work, you just need to import and use them), given that you always extract the most frequent ones and only push new ones?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would add a lot more complexity, wouldn't it?

Copy link
Collaborator

@appgurueu appgurueu Oct 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the API of our heap should be pretty straightforward to use. A heap is pretty much the data structure for this use case; it would significantly help the time complexity. If you want me to, I can make the necessary changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try to implement it myself, then I'll need your valuable feedback and guidance.

const left = nodes.shift()
const right = nodes.shift()
const parent = new HuffmanNode(null, left.freq + right.freq)
parent.left = left
parent.right = right
nodes.push(parent)
}

return nodes[0]
}

/**
Expand All @@ -76,14 +74,14 @@ function buildHuffmanTree(freqTable) {
* @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)
if (root) {
if (root.char) {
codes[root.char] = prefix
}
return codes
buildHuffmanCodes(root.left, prefix + '0', codes)
buildHuffmanCodes(root.right, prefix + '1', codes)
}
return codes
}

/**
Expand All @@ -93,15 +91,15 @@ function buildHuffmanCodes(root, prefix = '', codes = {}) {
* @returns {string} - The encoded string.
*/
function encodeHuffman(data, freqTable) {
const root = buildHuffmanTree(freqTable)
const codes = buildHuffmanCodes(root)
const root = buildHuffmanTree(freqTable)
const codes = buildHuffmanCodes(root)

let encodedData = ''
for (let char of data) {
encodedData += codes[char]
}
let encodedData = ''
for (let char of data) {
encodedData += codes[char]
}

return encodedData
return encodedData
}

/**
Expand All @@ -111,22 +109,22 @@ function encodeHuffman(data, freqTable) {
* @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
}
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
return decodedData
}

// Example usage
Expand All @@ -140,9 +138,9 @@ const decodedData = decodeHuffman(encodedData, root)
console.log('Decoded Data:', decodedData)

export {
buildHuffmanCodes,
buildHuffmanTree,
encodeHuffman,
decodeHuffman,
buildFrequencyTable
buildHuffmanCodes,
buildHuffmanTree,
encodeHuffman,
decodeHuffman,
buildFrequencyTable
}