-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathHuffmanCodeCompression.cs
More file actions
129 lines (101 loc) · 4.54 KB
/
HuffmanCodeCompression.cs
File metadata and controls
129 lines (101 loc) · 4.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
using System.Collections;
using System.Collections.Generic;
using System.Text;
using AlgorithmsAndDataStructures.DataStructures.BinaryHeaps;
namespace AlgorithmsAndDataStructures.Algorithms.Compression;
public class HuffmanCodeCompression
{
#pragma warning disable CA1822 // Mark members as static
public (BitArray Compressed, HuffmanCodeNode HuffmanEncodingTree) Compress(string target)
#pragma warning restore CA1822 // Mark members as static
{
if (string.IsNullOrEmpty(target)) return (new BitArray(0), null);
var frequencyMap = GetFrequencyMap(target);
var frequencyHeap = BuildFrequencyMinHeap(frequencyMap);
while (frequencyHeap.Size > 1)
{
var lowestFrequency = frequencyHeap.GetTop();
var secondLowestFrequency = frequencyHeap.GetTop();
var combinedFrequency = lowestFrequency.Frequency + secondLowestFrequency.Frequency;
var combinedSubtree = CreateSubTree(combinedFrequency, lowestFrequency, secondLowestFrequency);
frequencyHeap.Insert(combinedSubtree);
}
var huffmanEncodingTree = frequencyHeap.GetTop();
var huffmanEncodingMap = new Dictionary<char, List<bool>>();
BuildEncodingMap(huffmanEncodingTree, huffmanEncodingMap, new List<bool>());
var compressed = new List<bool>(target.Length);
foreach (var symbol in target) compressed.AddRange(huffmanEncodingMap[symbol]);
return (new BitArray(compressed.ToArray()), huffmanEncodingTree);
}
#pragma warning disable CA1822 // Mark members as static
public string Decompress(BitArray compressedTarget, HuffmanCodeNode huffmanEncodingTree)
#pragma warning restore CA1822 // Mark members as static
{
var currentBitPointer = 0;
var currentNode = huffmanEncodingTree;
var resultBuilder = new StringBuilder(compressedTarget?.Length ?? 0);
while (currentBitPointer <= compressedTarget?.Length)
{
if (currentNode?.Character.HasValue == true)
{
resultBuilder.Append(currentNode.Character.Value);
currentNode = huffmanEncodingTree;
}
if (currentBitPointer == compressedTarget.Length) break;
var currentBit = compressedTarget[currentBitPointer];
currentBitPointer++;
currentNode = currentBit == false ? currentNode?.Left : currentNode?.Right;
}
return resultBuilder.ToString();
}
private static void BuildEncodingMap(HuffmanCodeNode huffmanEncodingTree,
IDictionary<char, List<bool>> huffmanEncodingMap, List<bool> encoding)
{
if (huffmanEncodingTree is null) return;
if (huffmanEncodingTree.Character.HasValue)
{
huffmanEncodingMap[huffmanEncodingTree.Character.Value] = encoding;
return;
}
var leftEncoding = new List<bool>(encoding);
var rightEncoding = new List<bool>(encoding);
leftEncoding.Add(false);
rightEncoding.Add(true);
BuildEncodingMap(huffmanEncodingTree.Left, huffmanEncodingMap, leftEncoding);
BuildEncodingMap(huffmanEncodingTree.Right, huffmanEncodingMap, rightEncoding);
}
private static HuffmanCodeNode CreateSubTree(int combinedFrequency, HuffmanCodeNode highestFrequency,
HuffmanCodeNode secondHighestFrequency)
{
var combinedTreeHead =
new HuffmanCodeNode
{
Frequency = combinedFrequency,
Left = highestFrequency,
Right = secondHighestFrequency
};
return combinedTreeHead;
}
private static MinBinaryHeap<HuffmanCodeNode> BuildFrequencyMinHeap(Dictionary<char, int> frequencyMap)
{
var minHeap = new MinBinaryHeap<HuffmanCodeNode>(frequencyMap.Count);
foreach (var frequency in frequencyMap)
minHeap.Insert(
new HuffmanCodeNode
{
Character = frequency.Key,
Frequency = frequency.Value
});
return minHeap;
}
private static Dictionary<char, int> GetFrequencyMap(string target)
{
var frequencyMap = new Dictionary<char, int>();
foreach (var character in target)
{
if (!frequencyMap.ContainsKey(character)) frequencyMap.Add(character, 0);
frequencyMap[character] = frequencyMap[character] + 1;
}
return frequencyMap;
}
}