Skip to content

Instantly share code, notes, and snippets.

@jcttrll
Last active July 29, 2017 17:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jcttrll/8508cb22010a241ebab3f0b934d980a9 to your computer and use it in GitHub Desktop.
Save jcttrll/8508cb22010a241ebab3f0b934d980a9 to your computer and use it in GitHub Desktop.
Simple Huffman coding example for character strings
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import static java.lang.Math.min;
import static java.util.stream.Collectors.counting;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toCollection;
import static java.util.stream.Collectors.toMap;
public class Huffman {
public static void main (String[] args) {
if (args.length != 1) {
System.err.println("Usage: huffman <characters>");
System.exit(1);
}
huffmanCodes(args[0]).entrySet().forEach(
h -> System.out.println(h.getKey() + ": " + h.getValue())
);
}
private static Map<Character, String> huffmanCodes(String characters) {
Map<Character, Long> frequencies =
characters.chars()
.mapToObj(c -> Character.valueOf((char) c))
.collect(groupingBy(x -> x, counting()));
int discreteCharacterCount = frequencies.size();
PriorityQueue<Node> nodes =
frequencies.entrySet().stream()
.map(e -> new Node(e.getKey(), e.getValue()))
.collect(toCollection(() -> new PriorityQueue<Node>(discreteCharacterCount)));
while (nodes.size() > 1) {
nodes.add(new Branch(nodes.remove(), nodes.remove()));
}
// Output tree for debugging, if desired
//System.out.println(nodes.peek());
List<HuffmanCoding> codings = new ArrayList<>(discreteCharacterCount);
Deque<HuffmanCoding> stack = new ArrayDeque<>(discreteCharacterCount - 1);
stack.push(new HuffmanCoding("", nodes.remove()));
while (!stack.isEmpty()) {
HuffmanCoding current = stack.pop();
if (current.node instanceof Branch) {
Branch branch = (Branch) current.node;
stack.push(new HuffmanCoding(current.code + "1", branch.right));
stack.push(new HuffmanCoding(current.code + "0", branch.left));
} else {
codings.add(current);
}
}
return codings.stream().collect(toMap(h -> h.node.character, h -> h.code));
}
}
class Node implements Comparable<Node> {
final char character;
final long frequency;
Node(int character, long frequency) {
this.character = (char) character;
this.frequency = frequency;
}
@Override
public int compareTo(Node other) {
if (frequency > other.frequency) return 1;
if (frequency < other.frequency) return -1;
if (character > other.character) return 1;
if (character < other.character) return -1;
return 0;
}
@Override
public String toString() {
return "(" + character + "|" + frequency + ")";
}
}
class Branch extends Node {
final Node left;
final Node right;
Branch(Node a, Node b) {
super(min(a.character, b.character), a.frequency + b.frequency);
if (a.compareTo(b) > 0) {
this.left = b;
this.right = a;
} else {
this.left = a;
this.right = b;
}
}
@Override
public String toString() {
return "{left: " + left + ", right: " + right + "}";
}
}
class HuffmanCoding {
final String code;
final Node node;
HuffmanCoding(String code, Node node) {
this.code = code;
this.node = node;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment