-
- Notifications
You must be signed in to change notification settings - Fork 359
Add Huffman in C#. #82
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
Changes from 3 commits
85424b9
d91ea9e
5d5089a
664e3f9
233962f
ff95d09
2f79bb5
d2000db
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
// submitted by Julian Schacher (jspp) with help by gustorn. | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
| ||
namespace HuffmanCoding | ||
{ | ||
public class EncodingResult | ||
{ | ||
public List<bool> BitString { get; set; } | ||
| ||
public Dictionary<char, List<bool>> Dictionary { get; set; } | ||
public HuffmanCoding.Node Tree { get; set; } | ||
| ||
public EncodingResult(List<bool> bitString, Dictionary<char, List<bool>> dictionary, HuffmanCoding.Node tree) | ||
{ | ||
this.BitString = bitString; | ||
this.Dictionary = dictionary; | ||
this.Tree = tree; | ||
} | ||
} | ||
| ||
public static class HuffmanCoding | ||
{ | ||
// The Node class used for the Huffman Tree. | ||
public class Node : IComparable<Node> | ||
{ | ||
public Node LeftChild { get; set; } | ||
public Node RightChild { get; set; } | ||
public List<bool> BitString { get; set; } = new List<bool>(); | ||
public int Weight { get; set; } | ||
public string Key { get; set; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the key? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. Maybe There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The key of a node is string, so that a parent node's/branch's key is a combination of all it's children's keys. | ||
| ||
// Creates a leaf. So just a node is created with the given values. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These comments are totally superfluous There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why? They are explaining what is done pretty well. | ||
public static Node CreateLeaf(char key, int weight) => new Node(key.ToString(), weight, null, null); | ||
// Creates a branch. Here a node is created by adding the keys and weights of both childs together. | ||
public static Node CreateBranch(Node leftChild, Node rightChild) => new Node(leftChild.Key + rightChild.Key, leftChild.Weight + rightChild.Weight, leftChild, rightChild); | ||
private Node(string key, int weight, Node leftChild, Node rightChild) | ||
{ | ||
this.Key = key; | ||
this.Weight = weight; | ||
this.LeftChild = leftChild; | ||
this.RightChild = rightChild; | ||
} | ||
| ||
public int CompareTo(Node other) => this.Weight - other.Weight; | ||
} | ||
| ||
// Node with biggest value at the top. | ||
class NodePriorityList | ||
{ | ||
public int Count => nodes.Count; | ||
| ||
private List<Node> nodes = new List<Node>(); | ||
| ||
public NodePriorityList() { } | ||
public NodePriorityList(List<Node> givenNodes) | ||
{ | ||
this.nodes = givenNodes.ToList(); | ||
this.nodes.Sort(); | ||
} | ||
| ||
public void Add(Node newNode) | ||
{ | ||
var index = ~this.nodes.BinarySearch(newNode); | ||
| ||
if (index == this.nodes.Count) | ||
{ | ||
this.nodes.Add(newNode); | ||
return; | ||
} | ||
this.nodes.Insert(~index, newNode); | ||
} | ||
| ||
public Node Pop() | ||
{ | ||
var first = this.nodes.First(); | ||
| ||
if (first != null) | ||
this.nodes.Remove(first); | ||
return first; | ||
} | ||
} | ||
| ||
public static EncodingResult Encode(string input) | ||
{ | ||
var root = CreateTree(input); | ||
var dictionary = CreateDictionary(root); | ||
var bitString = CreateBitString(input, dictionary); | ||
| ||
return new EncodingResult(bitString, dictionary, root); | ||
} | ||
| ||
public static string Decode(EncodingResult result) | ||
{ | ||
var output = ""; | ||
Node currentNode = result.Tree; | ||
foreach (var boolean in result.BitString) | ||
| ||
{ | ||
// Go down the tree. | ||
if (!boolean) | ||
currentNode = currentNode.LeftChild; | ||
else | ||
currentNode = currentNode.RightChild; | ||
| ||
// Check if it's a leaf node. | ||
if (currentNode.Key.Count() == 1) | ||
| ||
{ | ||
output += currentNode.Key; | ||
currentNode = result.Tree; | ||
} | ||
} | ||
return output; | ||
} | ||
| ||
private static Node CreateTree(string input) | ||
{ | ||
// Create a List of all characters and their count in input by putting them into nodes. | ||
var nodes = input | ||
.GroupBy(c => c) | ||
.Select(n => Node.CreateLeaf(n.Key, n.Count())) | ||
.ToList(); | ||
| ||
// Convert list of nodes to a NodePriorityList. | ||
var nodePriorityList = new NodePriorityList(nodes); | ||
| ||
// Create Tree. | ||
while (nodePriorityList.Count > 1) | ||
{ | ||
// Pop the two nodes with the smallest weights from the nodePriorityList and create a parentNode with the CreateBranch method. (This method adds the keys and weights of the childs together.) | ||
var leftChild = nodePriorityList.Pop(); | ||
var rightChild = nodePriorityList.Pop(); | ||
var parentNode = Node.CreateBranch(leftChild, rightChild); | ||
| ||
nodePriorityList.Add(parentNode); | ||
} | ||
| ||
return nodePriorityList.Pop(); | ||
} | ||
| ||
private static Dictionary<char, List<bool>> CreateDictionary(Node root) | ||
| ||
{ | ||
var dictionary = new Dictionary<char, List<bool>>(); | ||
| ||
var stack = new Stack<Node>(); | ||
stack.Push(root); | ||
Node temp; | ||
| ||
while (stack.Count != 0) | ||
{ | ||
temp = stack.Pop(); | ||
| ||
if (temp.Key.Count() == 1) | ||
dictionary.Add(temp.Key[0], temp.BitString); | ||
else | ||
{ | ||
if (temp.LeftChild != null) | ||
{ | ||
temp.LeftChild.BitString.AddRange(temp.BitString); | ||
temp.LeftChild.BitString.Add(false); | ||
stack.Push(temp.LeftChild); | ||
} | ||
if (temp.RightChild != null) | ||
{ | ||
temp.RightChild.BitString.AddRange(temp.BitString); | ||
temp.RightChild.BitString.Add(true); | ||
stack.Push(temp.RightChild); | ||
} | ||
} | ||
} | ||
| ||
return dictionary; | ||
} | ||
| ||
private static List<bool> CreateBitString(string input, Dictionary<char, List<bool>> dictionary) | ||
{ | ||
var bitString = new List<bool>(); | ||
foreach (var character in input) | ||
bitString.AddRange(dictionary[character]); | ||
| ||
return bitString; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
// submitted by Julian Schacher (jspp) with help by gustorn. | ||
using System.Collections; | ||
using System.Collections.Generic; | ||
| ||
namespace HuffmanCoding | ||
{ | ||
class Program | ||
{ | ||
static void Main(string[] args) | ||
{ | ||
var result = HuffmanCoding.Encode("aaaabbbccd"); | ||
// Print dictionary. | ||
foreach (var entry in result.Dictionary) | ||
{ | ||
var bitString = ""; | ||
| ||
foreach (var value in entry.Value) | ||
{ | ||
if (value) | ||
bitString += "1"; | ||
else | ||
bitString += "0"; | ||
} | ||
System.Console.WriteLine(entry.Key + " " + bitString); | ||
} | ||
// Print bitString. | ||
var readableBitString = ""; | ||
foreach (var boolean in result.BitString) | ||
{ | ||
if (boolean) | ||
readableBitString += "1"; | ||
else | ||
readableBitString += "0"; | ||
} | ||
System.Console.WriteLine($"{readableBitString} count: {readableBitString.Length}"); | ||
| ||
var originalString = HuffmanCoding.Decode(result); | ||
System.Console.WriteLine(originalString); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't much more efficient than a regular
String
and it's much more annoying to debug and print. If you seriously want to produce a packed binary result then I'd go withSystem.Collections.BitArray
but I think a regularString
works better for educational purposes.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will go for the BitArray then. I don't think a string is fitting in this case, since no real compression would be achieved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The point isn't compression, it's showcasing thr algorithm. And there was no compression achieved with
List<bool>
either. I'd just go withString
(as almost all other implementations in the AAA do).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean since we usually save whole bytes anyway?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Strings in C# are UTF16 encoded which is 2 bytes. If you save the bitstring as a
List<bool>
that'sN
bytes, whereN
is the length of the bitstring. That means you literally only have any compression if the bitstring is a single bit long.