Skip to content

Added huffman code #21

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Algorithms.HuffmanCoding;
using System;
using System.Collections.Generic;
using System.Text;
using Xunit;

namespace Algorithms.Tests.HuffmanCodingTests
{
public class HuffmanCodingTests
{
[Fact]
public void TestPriorityQueue()
{
PriorityQueue<int> priorityQueue = new PriorityQueue<int>();
priorityQueue.Enqueue(10);
priorityQueue.Enqueue(20);
priorityQueue.Enqueue(5);
priorityQueue.Enqueue(100);

Assert.Equal(5, priorityQueue.Dequeue());
}

[Theory]
[InlineData("abaacaabd", "d", "011")]
[InlineData("aaaaaaaaaaaaaaaaaaaaaaaaaaac", "a", "1")]
Copy link
Member

Choose a reason for hiding this comment

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

In that case code for "a" should be 0, doesn't it?

[InlineData("cad", "acd", "11100")]
public void TestHuffmanCode(string data, string input, string expectedCode)
{
HuffmanCoding.HuffmanCoding huffmanCoding = new HuffmanCoding.HuffmanCoding();
Assert.Equal(expectedCode, huffmanCoding.GetHuffmanCode(data, input));
}
}
}
109 changes: 109 additions & 0 deletions Algorithms/Algorithms/HuffmanCoding/HuffmanCoding.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Algorithms.HuffmanCoding
{

public class HuffmanNode : IComparable<HuffmanNode>
Copy link
Member

@Rizzen Rizzen Oct 4, 2018

Choose a reason for hiding this comment

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

Public class should be placed in a separate file.

{
public long frequency;
Copy link
Member

Choose a reason for hiding this comment

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

Please follow the naming guideline

Copy link
Member

Choose a reason for hiding this comment

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

Also please rename "ch" to something more readable, like "character" or "symbol".

public char ch;

public HuffmanNode left;
public HuffmanNode right;

public HuffmanNode(char ch, long frequency)
{
this.ch = ch;
this.frequency = frequency;
}

public int CompareTo(HuffmanNode node)
{
return frequency.CompareTo(node.frequency);
}
}

public class HuffmanCoding
{
PriorityQueue<HuffmanNode> priorityQueue;
Copy link
Member

Choose a reason for hiding this comment

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

Please specify accessibility level explicitly

HuffmanNode root = null;
Dictionary<char, string> prefixCode = new Dictionary<char, string>();

internal void BuildHuffmanTree(Dictionary<char, long> charFrequency)
{
// Create a priority queue with capacity as dictionary size (no of unique chars)
priorityQueue = new PriorityQueue<HuffmanNode>(charFrequency.Count);

foreach (var item in charFrequency)
{
var node = new HuffmanNode(item.Key, item.Value);
priorityQueue.Enqueue(node);
}

while (priorityQueue.Count > 1)
{
var first = priorityQueue.Dequeue();
var second = priorityQueue.Dequeue();

var parent = new HuffmanNode('$', first.frequency + second.frequency);
parent.left = first;
parent.right = second;

root = parent;

priorityQueue.Enqueue(parent);
}
}

internal void GeneratePrefixCode(HuffmanNode node, string input)
{
if (node.left == null && node.right == null && Char.IsLetter(node.ch))
{
prefixCode.Add(node.ch, input);
return;
}

GeneratePrefixCode(node.left, input + "0");
GeneratePrefixCode(node.right, input + "1");
}

internal Dictionary<char, long> GetCharacterFrequency(string inputStr)
{
var charFrequency = new Dictionary<char, long>();

foreach (var ch in inputStr)
{
if (charFrequency.ContainsKey(ch))
charFrequency[ch]++;
else
charFrequency[ch] = 1;
}
return charFrequency;
}

public string GetHuffmanCode(string data, string input)
{
if (string.IsNullOrWhiteSpace(data))
throw new ArgumentNullException("Invalid data");
if (string.IsNullOrWhiteSpace(input))
throw new ArgumentNullException("Invalid input string");

string huffmanCode = string.Empty;

var charFrequency = GetCharacterFrequency(data);
BuildHuffmanTree(charFrequency);
GeneratePrefixCode(root, string.Empty);

if(prefixCode?.Count > 0)
{
foreach(var ch in input)
{
huffmanCode += prefixCode[ch];
}
}
return huffmanCode;
}
}
}
170 changes: 170 additions & 0 deletions Algorithms/Algorithms/HuffmanCoding/PriorityQueue.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Algorithms.HuffmanCoding
{
public sealed class PriorityQueue<T> where T : IComparable<T>
{
private long _count = long.MinValue;
private IndexedItem[] _items;
private int _size;

public PriorityQueue()
: this(16)
{
}

public PriorityQueue(int capacity)
{
_items = new IndexedItem[capacity];
_size = 0;
}

private bool IsHigherPriority(int left, int right)
{
return _items[left].CompareTo(_items[right]) < 0;
}

private int Percolate(int index)
{
if (index >= _size || index < 0)
{
return index;
}

var parent = (index - 1) / 2;
while (parent >= 0 && parent != index && IsHigherPriority(index, parent))
{
// swap index and parent
var temp = _items[index];
_items[index] = _items[parent];
_items[parent] = temp;

index = parent;
parent = (index - 1) / 2;
}

return index;
}

private void Heapify(int index)
{
if (index >= _size || index < 0)
{
return;
}

while (true)
{
var left = 2 * index + 1;
var right = 2 * index + 2;
var first = index;

if (left < _size && IsHigherPriority(left, first))
{
first = left;
}

if (right < _size && IsHigherPriority(right, first))
{
first = right;
}

if (first == index)
{
break;
}

// swap index and first
var temp = _items[index];
_items[index] = _items[first];
_items[first] = temp;

index = first;
}
}

public int Count => _size;

public T Peek()
{
if (_size == 0)
{
throw new InvalidOperationException("Empty Heap");
}

return _items[0].Value;
}

private void RemoveAt(int index)
{
_items[index] = _items[--_size];
_items[_size] = default;

if (Percolate(index) == index)
{
Heapify(index);
}

if (_size < _items.Length / 4)
{
var temp = _items;
_items = new IndexedItem[_items.Length / 2];
Array.Copy(temp, 0, _items, 0, _size);
}
}

public T Dequeue()
{
var result = Peek();
RemoveAt(0);
return result;
}

public void Enqueue(T item)
{
if (_size >= _items.Length)
{
var temp = _items;
_items = new IndexedItem[_items.Length * 2];
Array.Copy(temp, _items, temp.Length);
}

var index = _size++;
_items[index] = new IndexedItem { Value = item, Id = ++_count };
Percolate(index);
}

public bool Remove(T item)
{
for (var i = 0; i < _size; ++i)
{
if (EqualityComparer<T>.Default.Equals(_items[i].Value, item))
{
RemoveAt(i);
return true;
}
}

return false;
}

private struct IndexedItem : IComparable<IndexedItem>
{
public T Value;
public long Id;

public int CompareTo(IndexedItem other)
{
var c = Value.CompareTo(other.Value);
if (c == 0)
{
c = Id.CompareTo(other.Id);
}

return c;
}
}
}
}