More huffman.
This commit is contained in:
@@ -6,19 +6,11 @@ import tester.Tester;
|
|||||||
|
|
||||||
class Examples {
|
class Examples {
|
||||||
|
|
||||||
ITree a;
|
ITree a, b, c, d, e, f, g, h, i, j;
|
||||||
ITree b;
|
|
||||||
ITree c;
|
|
||||||
ITree d;
|
|
||||||
ITree e;
|
|
||||||
ITree f;
|
|
||||||
ITree g, h, i, j;
|
|
||||||
|
|
||||||
ArrayList<ITree> trees;
|
ArrayList<
|
||||||
ArrayList<ITree> treesSomeSorted;
|
ITree
|
||||||
ArrayList<ITree> treesFirstTwoSorted;
|
> trees, treesSomeSorted, treesFirstTwoSorted, treesSame, treesSorted;
|
||||||
ArrayList<ITree> treesSame;
|
|
||||||
ArrayList<ITree> treesSorted;
|
|
||||||
|
|
||||||
void init() {
|
void init() {
|
||||||
a = new Leaf(12, "a");
|
a = new Leaf(12, "a");
|
||||||
@@ -44,15 +36,18 @@ class Examples {
|
|||||||
Util.insertIntoSorted(c, treesSomeSorted);
|
Util.insertIntoSorted(c, treesSomeSorted);
|
||||||
t.checkExpect(
|
t.checkExpect(
|
||||||
treesSomeSorted,
|
treesSomeSorted,
|
||||||
new ArrayList<ITree>(Arrays.asList(c, e, d, f)));
|
new ArrayList<ITree>(Arrays.asList(c, e, d, f))
|
||||||
|
);
|
||||||
Util.insertIntoSorted(a, treesSomeSorted);
|
Util.insertIntoSorted(a, treesSomeSorted);
|
||||||
t.checkExpect(
|
t.checkExpect(
|
||||||
treesSomeSorted,
|
treesSomeSorted,
|
||||||
new ArrayList<ITree>(Arrays.asList(c, e, a, d, f)));
|
new ArrayList<ITree>(Arrays.asList(c, e, a, d, f))
|
||||||
|
);
|
||||||
Util.insertIntoSorted(b, treesSomeSorted);
|
Util.insertIntoSorted(b, treesSomeSorted);
|
||||||
t.checkExpect(
|
t.checkExpect(
|
||||||
treesSomeSorted,
|
treesSomeSorted,
|
||||||
new ArrayList<ITree>(Arrays.asList(c, e, a, d, f, b)));
|
new ArrayList<ITree>(Arrays.asList(c, e, a, d, f, b))
|
||||||
|
);
|
||||||
Util.insertIntoSorted(j, treesSame);
|
Util.insertIntoSorted(j, treesSame);
|
||||||
t.checkExpect(treesSame, new ArrayList<ITree>(Arrays.asList(h, i, j)));
|
t.checkExpect(treesSame, new ArrayList<ITree>(Arrays.asList(h, i, j)));
|
||||||
}
|
}
|
||||||
@@ -62,11 +57,13 @@ class Examples {
|
|||||||
Util.insert(a, treesFirstTwoSorted, 2);
|
Util.insert(a, treesFirstTwoSorted, 2);
|
||||||
t.checkExpect(
|
t.checkExpect(
|
||||||
treesFirstTwoSorted,
|
treesFirstTwoSorted,
|
||||||
new ArrayList<ITree>(Arrays.asList(c, e, a, b, f)));
|
new ArrayList<ITree>(Arrays.asList(c, e, a, b, f))
|
||||||
|
);
|
||||||
Util.insert(g, treesFirstTwoSorted, 3);
|
Util.insert(g, treesFirstTwoSorted, 3);
|
||||||
t.checkExpect(
|
t.checkExpect(
|
||||||
treesFirstTwoSorted,
|
treesFirstTwoSorted,
|
||||||
new ArrayList<ITree>(Arrays.asList(c, e, a, g, b, f)));
|
new ArrayList<ITree>(Arrays.asList(c, e, a, g, b, f))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void testUtilSort(Tester t) {
|
void testUtilSort(Tester t) {
|
||||||
@@ -79,37 +76,126 @@ class Examples {
|
|||||||
Util.sort(trees);
|
Util.sort(trees);
|
||||||
t.checkExpect(trees, treesSorted);
|
t.checkExpect(trees, treesSorted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void testUtilEnlist(Tester t) {
|
||||||
|
String s = "Hello";
|
||||||
|
t.checkExpect(
|
||||||
|
Util.enlist(s),
|
||||||
|
new ArrayList<String>(Arrays.asList("H", "e", "l", "l", "o"))
|
||||||
|
);
|
||||||
|
|
||||||
|
String s2 = "h";
|
||||||
|
t.checkExpect(
|
||||||
|
Util.enlist(s2),
|
||||||
|
new ArrayList<String>(Arrays.asList("h"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testHuffmanMkcode(Tester t) {
|
||||||
|
init();
|
||||||
|
Huffman h = new Huffman(
|
||||||
|
new ArrayList<String>(Arrays.asList("a", "b")),
|
||||||
|
new ArrayList<Integer>(Arrays.asList(1, 1))
|
||||||
|
);
|
||||||
|
|
||||||
|
t.checkExpect(h.code, new Node(2, new Leaf(1, "a"), new Leaf(1, "b")));
|
||||||
|
|
||||||
|
Huffman h2 = new Huffman(
|
||||||
|
new ArrayList<String>(Arrays.asList("a", "b", "c", "d", "e")),
|
||||||
|
new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4, 5))
|
||||||
|
);
|
||||||
|
|
||||||
|
// 1: a:1, b:2, c:3, d:4, e:5
|
||||||
|
// 2: (a:1|b:2):3, c:3, d:4, e:5
|
||||||
|
// 3: d:4, e:5, ((a:1|b:2):3|c:3):6
|
||||||
|
// 4: ((a:1|b:2):3|c:3):6, (d:4|e:5):9
|
||||||
|
// 5: (((a:1|b:2):3|:3):6|(d:4|e:5):9):15
|
||||||
|
|
||||||
|
t.checkExpect(
|
||||||
|
h2.code,
|
||||||
|
new Node(
|
||||||
|
15,
|
||||||
|
new Node(
|
||||||
|
6,
|
||||||
|
new Node(3, new Leaf(1, "a"), new Leaf(2, "b")),
|
||||||
|
new Leaf(3, "c")
|
||||||
|
),
|
||||||
|
new Node(9, new Leaf(4, "d"), new Leaf(5, "e"))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Huffman {
|
class Huffman {
|
||||||
|
|
||||||
ArrayList<String> charset; // Character set.
|
ArrayList<String> charset; // Character set.
|
||||||
ArrayList<Integer> freqs; // Frequencies of characters in charset.
|
ArrayList<Integer> freqs; // Frequencies of characters in charset.
|
||||||
|
ITree code; // The tree allowing for encoding and decoding.
|
||||||
|
|
||||||
Huffman(ArrayList<String> charset, ArrayList<Integer> freqs) {
|
Huffman(ArrayList<String> charset, ArrayList<Integer> freqs) {
|
||||||
if (charset.size() != freqs.size())
|
if (charset.size() != freqs.size()) throw new IllegalArgumentException(
|
||||||
throw new IllegalArgumentException(
|
"Character set must match frequencies."
|
||||||
"Character set must match frequencies.");
|
);
|
||||||
|
|
||||||
if (charset.size() < 2)
|
if (charset.size() < 2) throw new IllegalArgumentException(
|
||||||
throw new IllegalArgumentException(
|
"Character set too small."
|
||||||
"Character set too small.");
|
);
|
||||||
|
|
||||||
this.charset = charset;
|
this.charset = charset;
|
||||||
this.freqs = freqs;
|
this.freqs = freqs;
|
||||||
|
|
||||||
|
this.mkcode();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Creates the code tree.
|
||||||
* // Encode a message.
|
void mkcode() {
|
||||||
* ArrayList<Boolean> encode(String s) {
|
ArrayList<ITree> nodes = new ArrayList<>();
|
||||||
* for
|
|
||||||
* }
|
// Copy all chars over as a tree leaves.
|
||||||
*
|
for (int i = 0; i < this.charset.size(); i++) nodes.add(
|
||||||
* // Decode a message.
|
new Leaf(this.freqs.get(i), this.charset.get(i))
|
||||||
* String decode(ArrayList<Boolean> c) {
|
);
|
||||||
*
|
|
||||||
* }
|
// Combine until all under a single tree.
|
||||||
*/
|
while (nodes.size() > 1) {
|
||||||
|
// Sort the nodes.
|
||||||
|
Util.sort(nodes);
|
||||||
|
|
||||||
|
// Move the 2 lowest into their own tree.
|
||||||
|
ITree firstLowest = nodes.get(0);
|
||||||
|
ITree secondLowest = nodes.get(1);
|
||||||
|
nodes.remove(firstLowest);
|
||||||
|
nodes.remove(secondLowest);
|
||||||
|
ITree parent = new Node(
|
||||||
|
firstLowest.sumFreq(secondLowest),
|
||||||
|
firstLowest,
|
||||||
|
secondLowest
|
||||||
|
);
|
||||||
|
|
||||||
|
// Put new tree into list.
|
||||||
|
nodes.add(0, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the code to the first (and hopefully only) tree.
|
||||||
|
this.code = nodes.getFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode a message.
|
||||||
|
ArrayList<Boolean> encode(String s) {
|
||||||
|
ArrayList<String> msg = Util.enlist(s); // The given message, as a list of characters.
|
||||||
|
ArrayList<Boolean> enc = new ArrayList<>(); // The returned encoded message.
|
||||||
|
|
||||||
|
// Loop for each character in the message.
|
||||||
|
for (String ch : msg) {
|
||||||
|
// Throw error if character is not in charset.
|
||||||
|
if (!this.charset.contains(ch)) throw new IllegalArgumentException(
|
||||||
|
"Cannot encode unknown character \"" + ch + "\"."
|
||||||
|
);
|
||||||
|
// Go through the
|
||||||
|
}
|
||||||
|
|
||||||
|
return enc;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Binary tree.
|
// Binary tree.
|
||||||
@@ -119,6 +205,13 @@ interface ITree {
|
|||||||
|
|
||||||
// Is this tree less than the given frequency?
|
// Is this tree less than the given frequency?
|
||||||
boolean lessThan(int freq);
|
boolean lessThan(int freq);
|
||||||
|
|
||||||
|
// Sum the frequency with another tree.
|
||||||
|
int sumFreq(ITree t);
|
||||||
|
int sumFreq(int freq);
|
||||||
|
|
||||||
|
// Given the character, choose whether to go left (0) or right (1) or stop (-1).
|
||||||
|
Int getNext(String ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class ATree implements ITree {
|
abstract class ATree implements ITree {
|
||||||
@@ -136,18 +229,39 @@ abstract class ATree implements ITree {
|
|||||||
public boolean lessThan(int freq) {
|
public boolean lessThan(int freq) {
|
||||||
return this.freq < freq;
|
return this.freq < freq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int sumFreq(ITree t) {
|
||||||
|
return t.sumFreq(this.freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int sumFreq(int freq) {
|
||||||
|
return this.freq + freq;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Node extends ATree {
|
class Node extends ATree {
|
||||||
|
|
||||||
// Left and right branches of tree.
|
// Left (false) and right (true) branches of tree.
|
||||||
ITree l, r;
|
ITree l, r;
|
||||||
|
|
||||||
|
// The 'question' the node asks to determine whether to go left or right. If
|
||||||
|
// the character is in the string, go right.
|
||||||
|
String question;
|
||||||
|
|
||||||
Node(int freq, ITree l, ITree r) {
|
Node(int freq, ITree l, ITree r) {
|
||||||
super(freq);
|
super(freq);
|
||||||
this.l = l;
|
this.l = l;
|
||||||
this.r = r;
|
this.r = r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ask its question on a character.
|
||||||
|
Boolean ask(String ch) {
|
||||||
|
return this.question.contains(ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer getNext(String ch) {
|
||||||
|
return this.ask(ch) ?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Leaf extends ATree {
|
class Leaf extends ATree {
|
||||||
@@ -165,8 +279,7 @@ class Util {
|
|||||||
// Sort a list of trees from least to most frequent.
|
// Sort a list of trees from least to most frequent.
|
||||||
static void sort(ArrayList<ITree> trees) {
|
static void sort(ArrayList<ITree> trees) {
|
||||||
// Our work here is done.
|
// Our work here is done.
|
||||||
if (trees.size() < 2)
|
if (trees.size() < 2) return;
|
||||||
return;
|
|
||||||
|
|
||||||
for (int i = 1; i < trees.size(); i++) {
|
for (int i = 1; i < trees.size(); i++) {
|
||||||
// Copy out the tree to insert.
|
// Copy out the tree to insert.
|
||||||
@@ -185,18 +298,27 @@ class Util {
|
|||||||
// Insert tree.
|
// Insert tree.
|
||||||
insertIntoSorted(tree, sorted);
|
insertIntoSorted(tree, sorted);
|
||||||
// Overwrite original trees with sorted ones.
|
// Overwrite original trees with sorted ones.
|
||||||
for (int j = 0; j < i; trees.set(j, sorted.get(j++)))
|
for (int j = 0; j < i; trees.set(j, sorted.get(j++)));
|
||||||
;
|
|
||||||
trees.add(i, sorted.getLast());
|
trees.add(i, sorted.getLast());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert a Tree in an already fully sorted ArrayList of Trees.
|
// Insert a Tree in an already fully sorted ArrayList of Trees.
|
||||||
static void insertIntoSorted(ITree tree, ArrayList<ITree> sortedTrees) {
|
static void insertIntoSorted(ITree tree, ArrayList<ITree> sortedTrees) {
|
||||||
for (int i = 0; i < sortedTrees.size(); i++)
|
for (int i = 0; i < sortedTrees.size(); i++) if (
|
||||||
if (sortedTrees.get(i).lessThan(tree)) {
|
sortedTrees.get(i).lessThan(tree)
|
||||||
|
) {
|
||||||
sortedTrees.add(i, tree); //
|
sortedTrees.add(i, tree); //
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sortedTrees.addLast(tree); // This tree is bigger than all the others.
|
sortedTrees.addLast(tree); // This tree is bigger than all the others.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert a string to a list.
|
||||||
|
static ArrayList<String> enlist(String s) {
|
||||||
|
ArrayList<String> ret = new ArrayList<>();
|
||||||
|
for (int i = 0; i < s.length(); i++) ret.add(
|
||||||
|
String.valueOf(s.charAt(i)) // Why why why can't we just use a list of chars ;_;
|
||||||
|
);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user