forked from TheAlgorithms/Java
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathHuffmanCoding.java
More file actions
169 lines (144 loc) · 5.73 KB
/
HuffmanCoding.java
File metadata and controls
169 lines (144 loc) · 5.73 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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;
// A node in the Huffman Tree
class HuffmanNode implements Comparable<HuffmanNode> {
int frequency;
char character;
HuffmanNode left;
HuffmanNode right;
// Compare nodes based on frequency. Used by the PriorityQueue.
@Override
public int compareTo(HuffmanNode other) {
return this.frequency - other.frequency;
}
}
public final class HuffmanCoding {
private static Map<Character, String> huffmanCodes = new HashMap<>();
private static HuffmanNode root;
private HuffmanCoding() {
throw new UnsupportedOperationException("Utility class");
}
/**
* Builds the Huffman Tree and generates codes.
* @param text The input string to be encoded.
*/
public static void buildTree(String text) {
// 1. Calculate character frequencies
Map<Character, Integer> frequencies = new HashMap<>();
for (char character : text.toCharArray()) {
frequencies.put(character, frequencies.getOrDefault(character, 0) + 1);
}
// 2. Create leaf nodes and add them to a priority queue
PriorityQueue<HuffmanNode> pq = new PriorityQueue<>();
for (Map.Entry<Character, Integer> entry : frequencies.entrySet()) {
HuffmanNode node = new HuffmanNode();
node.character = entry.getKey();
node.frequency = entry.getValue();
node.left = null;
node.right = null;
pq.add(node);
}
// Handle case of single unique character
if (pq.size() == 1) {
HuffmanNode singleNode = pq.peek();
huffmanCodes.put(singleNode.character, "0");
root = singleNode;
return;
}
// 3. Build the tree by merging nodes
while (pq.size() > 1) {
// Get the two nodes with the lowest frequency
HuffmanNode left = pq.poll();
HuffmanNode right = pq.poll();
// Create a new internal node
HuffmanNode internalNode = new HuffmanNode();
internalNode.frequency = left.frequency + right.frequency;
internalNode.character = '-'; // Internal nodes have no character
internalNode.left = left;
internalNode.right = right;
// Add the new node back to the queue
pq.add(internalNode);
}
// The remaining node is the root of the tree
root = pq.poll();
// 4. Generate Huffman codes by traversing the tree
generateCodes(root, "");
}
/**
* Recursively traverses the tree to generate codes for each character.
* @param node The current node in the traversal.
* @param code The binary code generated so far.
*/
private static void generateCodes(HuffmanNode node, String code) {
if (node == null) {
return;
}
// If it's a leaf node, it contains a character
if (node.left == null && node.right == null) {
huffmanCodes.put(node.character, code);
return;
}
// Traverse left (append '0') and right (append '1')
generateCodes(node.left, code + "0");
generateCodes(node.right, code + "1");
}
/**
* Encodes the given text using the generated Huffman codes.
* @param text The text to encode.
* @return The encoded binary string.
*/
public static String encode(String text) {
StringBuilder encodedText = new StringBuilder();
for (char character : text.toCharArray()) {
encodedText.append(huffmanCodes.get(character));
}
return encodedText.toString();
}
/**
* Decodes the given binary string using the Huffman Tree.
* @param encodedText The binary string to decode.
* @return The original decoded text.
*/
public static String decode(String encodedText) {
StringBuilder decodedText = new StringBuilder();
HuffmanNode current = root;
for (int i = 0; i < encodedText.length(); i++) {
char bit = encodedText.charAt(i);
if (bit == '0') {
current = current.left;
} else {
current = current.right;
}
// If it's a leaf node, we found a character
if (current.left == null && current.right == null) {
decodedText.append(current.character);
current = root; // Return to the root for the next character
}
}
return decodedText.toString();
}
public static void main(String[] args) {
String text = "huffman coding is a lossless data compression algorithm";
System.out.println("Original Text: " + text);
System.out.println("----------------------------------------");
// Build the Huffman Tree and generate codes
buildTree(text);
// Print the Huffman codes for each character
System.out.println("Huffman Codes:");
for (Map.Entry<Character, String> entry : huffmanCodes.entrySet()) {
System.out.println("'" + entry.getKey() + "': " + entry.getValue());
}
System.out.println("----------------------------------------");
// Encode the text
String encodedText = encode(text);
System.out.println("Encoded Text: " + encodedText);
System.out.println("----------------------------------------");
// Decode the text
String decodedText = decode(encodedText);
System.out.println("Decoded Text: " + decodedText);
System.out.println("----------------------------------------");
// Verification
System.out.println("Verification (Original equals Decoded): " + text.equals(decodedText));
}
}