Hey peeps! 🌟 Let’s dive into the wild world of Huffman coding — a cool trick computers use to shrink data and make it travel faster. Think of it as the superhero of compression, saving space and time like a digital wizard! 🧙♂️
What’s the Huffman Hustle?
Imagine you’re sending texts, but each letter costs you money. Huffman coding is like texting smart — using fewer characters for the common letters (like ‘E’ or ‘A’) and more characters for the rare ones (like ‘Z’ or ‘Q’). It’s all about being efficient and saving your texting budget!
Meet the Huffman Tree 🌳
So, there’s this thing called the Huffman tree. It’s like a family tree for letters but super optimized. Frequent letters, the popular kids, are higher up in the tree, and the not-so-cool ones are at the bottom. The tree helps us create secret codes for each letter — short codes for popular ones and longer ones for the less popular.
Dance of 0s and 1s 💃🕺
As we build our Huffman tree, each move to the left or right is like a dance step — a left is a 0, a right is a 1. So, the path to a letter is like a secret dance move, and that’s its code! Short dance for the cool letters, longer dance for the less cool.
Crunching Numbers: Encoding and Decoding 🤓
Now, imagine you’re texting a friend using these secret codes. You replace each letter with its dance move, making your text shorter and snazzier. On the other end, your friend decodes it using the same dance moves, and voilà — your message is back!
Where’s the Party At? Real-World Coolness 🎉
Huffman coding is like the VIP pass for the digital party. It’s in everything from zipping files on your computer to sending pics on Snapchat. The cool part? It saves space and makes things speedy — exactly what we need in the digital age!
Wrapping it Up: Huffman Magic ✨
So, Huffman coding is like the superhero of saving space and making data move at warp speed. It’s the secret language of computers, making sure your texts, pics, and files are like ninjas — fast, efficient, and ready for the digital dance floor! 🚀💻📱
import java.util.HashMap;
import java.util.*;
class HuffmanEncoderDecoder {
HashMap<Character,String> encoder;
HashMap<String,Character> decoder;
private class Node implements Comparable<Node>{
Character data;
int cost; //frequency
Node left;
Node right;
public Node(Character data,int cost){
this.data=data;
this.cost=cost;
this.left=null;
this.right=null;
}
@Override
public int compareTo(Node other){
return this.cost- other.cost;
}
}
public HuffmanEncoderDecoder(String feeder) throws Exception{
HashMap<Character,Integer> fmap=new HashMap<>();
for(int i=0;i<feeder.length();i++){
char cc=feeder.charAt(i);
if(fmap.containsKey(cc)){
int ov=fmap.get(cc);
ov+=1;
fmap.put(cc,ov);
}else{
fmap.put(cc,1);
}
}
Heap<Node> minHeap = new Heap<>();
Set<Map.Entry<Character,Integer>>entrySet=fmap.entrySet();
for(Map.Entry<Character,Integer>entry:entrySet){
Node node=new Node(entry.getKey(),entry.getValue());
minHeap.insert(node);
}
while(minHeap.size()!=1){
Node first=minHeap.remove();
Node second=minHeap.remove();
Node newNode=new Node('\0', first.cost+ second.cost);
newNode.left=first;
newNode.right=second;
minHeap.insert(newNode);
}
Node ft=minHeap.remove();
this.encoder=new HashMap<>();
this.decoder=new HashMap<>();
this.initalizingEncoderDecoder(ft,"");
}
private void initalizingEncoderDecoder(Node node,String osf){
if(node==null){
return;
}
if(node.left==null && node.right==null){
this.encoder.put(node.data,osf);
this.decoder.put(osf,node.data);
}
initEncoderDecoder(node.left,osf+"0");
initEncoderDecoder(node.right,osf+"1");
}
public String encode(String source){
String ans="";
for(int i=0;i<source.length();i++){
ans=ans+encoder.get(source.charAt(i));
}
return ans;
}
public String decode(String encodedString){
String key="";
String ans="";
for(int i=0;i<encodedString.length();i++){
key+=encodedString.charAt(i);
if(decoder.containsKey(key)){
ans+=decoder.get(key);
key="";
}
}
return ans;
}
}
class HuffmanEncoderDecoder {
HashMap<Character, String> encoder;
HashMap<String, Character> decoder;
Defines a class named HuffmanEncoderDecoder
that will be used for Huffman encoding and decoding. It has two HashMaps, encoder
for encoding characters to binary strings and decoder
for decoding binary strings to characters.
private class Node implements Comparable<Node> {
Character data;
int cost; // frequency
Node left;
Node right;
public Node(Character data, int cost) {
this.data = data;
this.cost = cost;
this.left = null;
this.right = null;
}
Defines a private inner class named Node
to represent nodes of the Huffman tree. Each node has a character (data
), a frequency (cost
), and references to its left and right children.
@Override
public int compareTo(Node other) {
return this.cost - other.cost;
}
}
Overrides the compareTo
method to implement the Comparable
interface. This allows instances of the Node
class to be compared based on their frequencies.
public HuffmanEncoderDecoder(String feeder) throws Exception {
HashMap<Character, Integer> fmap = new HashMap<>();
for (int i = 0; i < feeder.length(); i++) {
char cc = feeder.charAt(i);
if (fmap.containsKey(cc)) {
int ov = fmap.get(cc);
ov += 1;
fmap.put(cc, ov);
} else {
fmap.put(cc, 1);
}
}
Constructor of HuffmanEncoderDecoder
class. Takes a string feeder
as input and initializes a frequency map (fmap
) for each character in the input string.
Heap<Node> minHeap = new Heap<>();
Set<Map.Entry<Character, Integer>> entrySet = fmap.entrySet();
for (Map.Entry<Character, Integer> entry : entrySet) {
Node node = new Node(entry.getKey(), entry.getValue());
minHeap.insert(node);
}
Creates a min-heap (Heap<Node> minHeap
) and inserts nodes representing characters and their frequencies into the heap.
while (minHeap.size() != 1) {
Node first = minHeap.remove();
Node second = minHeap.remove();
Node newNode = new Node('\0', first.cost + second.cost);
newNode.left = first;
newNode.right = second;
minHeap.insert(newNode);
}
Builds the Huffman tree by repeatedly combining the two nodes with the lowest frequencies until there is only one node remaining in the min-heap.
Node ft = minHeap.remove();
this.encoder = new HashMap<>();
this.decoder = new HashMap<>();
this.initEncoderDecoder(ft, "");
}
Sets the root of the Huffman tree as ft
and initializes empty encoder
and decoder
HashMaps. Calls the initEncoderDecoder
method to populate these maps.
private void initEncoderDecoder(Node node, String osf) {
if (node == null) {
return;
}
if (node.left == null && node.right == null) {
this.encoder.put(node.data, osf);
this.decoder.put(osf, node.data);
}
initEncoderDecoder(node.left, osf + "0");
initEncoderDecoder(node.right, osf + "1");
}
Recursively initializes the encoder
and decoder
maps by traversing the Huffman tree. The osf
(output so far) string is used to build binary representations in the encoder
map and to decode binary strings in the decoder
map.
public String encode(String source) {
String ans = "";
for (int i = 0; i < source.length(); i++) {
ans = ans + encoder.get(source.charAt(i));
}
return ans;
}
Encodes a given string source
using the encoder
map and returns the encoded string.
public String decode(String encodedString) {
String key = "";
String ans = "";
for (int i = 0; i < encodedString.length(); i++) {
key += encodedString.charAt(i);
if (decoder.containsKey(key)) {
ans += decoder.get(key);
key = "";
}
}
return ans;
}
}
Decodes a given encoded string using the decoder
map and returns the decoded string. It iterates through the encoded string, building a key until a valid mapping is found in the decoder
map, at which point it appends the corresponding character to the result and resets the key.