目录:
21.二叉树的深度遍历的递归实现
二叉树是一种重要的树形结构,二叉树特点是每个结点最多只能有两棵子树,且有左右之分。
一个二叉树的结点有数据域、左孩子域、右孩子域。
public My(char paraName) {
value = paraName;
leftChild = null;
rightChild = null;
}
手动建立一个二叉树:
public static My manualConstructTree() {
My resultTree = new My('a');
My tempTreeB = new My('b');
My tempTreeC = new My('c');
My tempTreeD = new My('d');
My tempTreeE = new My('e');
My tempTreeF = new My('f');
My tempTreeG = new My('g');
resultTree.leftChild = tempTreeB;
resultTree.rightChild = tempTreeC;
tempTreeB.rightChild = tempTreeD;
tempTreeC.leftChild = tempTreeE;
tempTreeD.leftChild = tempTreeF;
tempTreeD.rightChild = tempTreeG;
return resultTree;
}
建立后的二叉树如图所示:(深度为4,结点数为7)
使用递归的方法遍历二叉树
先序遍历:根左右,先访问根结点,然后左结点、右结点。先序遍历的结果 a b d f g c e
public void preOrderVisit() {
System.out.print("" + value + " ");
if (leftChild != null) {
leftChild.preOrderVisit();
}
if (rightChild != null) {
rightChild.preOrderVisit();
}
}
中序遍历:左根右,先访问左结点,再访问根结点,最后访问右结点。中序遍历的结果 b f d g a e c
public void inOrderVisit() {
if (leftChild != null) {
leftChild.inOrderVisit();
}
System.out.print("" + value + " ");
if (rightChild != null) {
rightChild.inOrderVisit();
}
}
后序遍历:左右根,先访问左结点、右结点,最后访问根结点。后序遍历的结果 f g d b e c a
public void postOrderVisit() {
if (leftChild != null) {
leftChild.postOrderVisit();
}
if (rightChild != null) {
rightChild.postOrderVisit();
}
System.out.print("" + value + " ");
}
计算二叉树的深度:先计算左子树的深度,再计算右子树的深度,当访问到叶子结点时返回深度1,如果左子树比右子树深则返回左子树深度+1,否则返回右子树深度+1.
public int getDepth() {
if ((leftChild == null) && (rightChild == null)) {
return 1;
}
int tempLeftDepth = 0,tempRightDepth = 0;
if (leftChild != null) {
tempLeftDepth = leftChild.getDepth();
}
if (rightChild != null) {
tempRightDepth = rightChild.getDepth();
}
if (tempLeftDepth >= tempRightDepth) {
return tempLeftDepth + 1;
} else {
return tempRightDepth + 1;
}
}
计算二叉树的结点数:和计算深度原理一样,先计算左子树的结点数,再计算右子树的结点数,最后返回左子树的结点加右子树的结点加1(表示当前结点),访问到叶子结点时,返回结点数1。
public int getNumNodes() {
if ((leftChild == null) && (rightChild == null)) {
return 1;
}
int tempLeftNodes = 0,tempRightNodes = 0;
if (leftChild != null) {
tempLeftNodes = leftChild.getNumNodes();
}
if (rightChild != null) {
tempRightNodes = rightChild.getNumNodes();
}
return tempLeftNodes + tempRightNodes + 1;
}
22.二叉树的存储
从完全满二叉树的广度优先遍历(bfs)角度来考虑这个问题: 每个节点都有一个 data 及其在二叉树中的位置.令根节点的位置为 0; 则第 2 层节点的位置依次为 1 至 2; 第 3 层节点的位置依次为 3 至 6. 以此类推.
在第21天的树中: 空树使用 0 来表示, 可以用一个向量来存储: [a, b, c, 0, d, e, 0, 0, 0, f, g]
优点: 仅需要一个向量, 简单直接.
缺点: 对于实际的二叉树, 很多子树为空, 导致大量的 0 值.刘知鑫指出: 应使用压缩存储方式, 即将节点的位置和值均存储.
可表示为两个向量:
[0, 1, 2, 4, 5, 9, 10]
[a, b, c, d, e, f, g]
使用两个队列(队列参考第18天代码)来对二叉树进行层次遍历,分别存储二叉树结点的位置与值:
将二叉树从根结点开始依此入队,左子树的下标为父结点下标×2+1,右子树的下标为父结点下标×2+2.
CircleObjectQueue tempQueue = new CircleObjectQueue();
tempQueue.enqueue(this);
CircleIntQueue tempIntQueue = new CircleIntQueue();
tempIntQueue.enqueue(0);
BinaryCharTree tempTree = (BinaryCharTree) tempQueue.dequeue();
int tempIndex = tempIntQueue.dequeue();
while (tempTree != null) {
valuesArray[i] = tempTree.value;
indicesArray[i] = tempIndex;
i++;
if (tempTree.leftChild != null) {
tempQueue.enqueue(tempTree.leftChild);
tempIntQueue.enqueue(tempIndex * 2 + 1);
}
if (tempTree.rightChild != null) {
tempQueue.enqueue(tempTree.rightChild);
tempIntQueue.enqueue(tempIndex * 2 + 2);
}
tempTree = (BinaryCharTree) tempQueue.dequeue();
tempIndex = tempIntQueue.dequeue();
}
运行结果:
23.使用通用性的队列
1、Object类是所有Java类的根基类(父类),如果在类的声明中未使用extends关键字指明其基类,则默认基类是Object类。
2、存储对象,实际上是存储对象的地址 (引用、指针),因此,可以存储任何类的对象 (的引用)。
3、可以通过强制类型转换将对象转成其本身的类别,例如程序中:括号中表示将出队的数据转换为BinaryCharTree类的数据。
CircleObjectQueue tempQueue = new CircleObjectQueue();
tempQueue.enqueue(this);
BinaryCharTree tempTree = (BinaryCharTree) tempQueue.dequeue();
4、括号中的类型即表示强制类型转换。Java将 int, double, char 分别封装到 Integer, Double, Char 类。
代码:
public class CircleObjectQueue {
public static void main(String args[]) {
CircleObjectQueue tempQueue = new CircleObjectQueue();
}
public static final int TOTAL_SPACE = 10;
Object[] data;
int head;
int tail;
public CircleObjectQueue() {
data = new Object[TOTAL_SPACE];
head = 0;
tail = 0;
}
public void enqueue(Object paraValue) {
if ((tail + 1) % TOTAL_SPACE == head) {
System.out.println("Queue full.");
return;
}
data[tail % TOTAL_SPACE] = paraValue;
tail++;
}
public Object dequeue() {
if (head == tail) {
return null;
}
Object resultValue = data[head];
head++;
return resultValue;
}
public String toString() {
String resultString = "";
if (head == tail) {
return "empty";
}
for (int i = head; i < tail; i++) {
resultString += data[i % TOTAL_SPACE] + ", ";
}
return resultString;
}
}
public void toDataArraysObjectQueue() {
int tempLength = getNumNodes();
valuesArray = new char[tempLength];
indicesArray = new int[tempLength];
int i = 0;
CircleObjectQueue tempQueue = new CircleObjectQueue();
tempQueue.enqueue(this);
CircleObjectQueue tempIntQueue = new CircleObjectQueue();
Integer tempIndexInteger = new Integer(0);
tempIntQueue.enqueue(tempIndexInteger);
BinaryCharTree tempTree = (BinaryCharTree) tempQueue.dequeue();
int tempIndex = ((Integer) tempIntQueue.dequeue()).intValue();
System.out.println("tempIndex = " + tempIndex);
while (tempTree != null) {
valuesArray[i] = tempTree.value;
indicesArray[i] = tempIndex;
i++;
if (tempTree.leftChild != null) {
tempQueue.enqueue(tempTree.leftChild);
tempIntQueue.enqueue(new Integer(tempIndex * 2 + 1));
}
if (tempTree.rightChild != null) {
tempQueue.enqueue(tempTree.rightChild);
tempIntQueue.enqueue(new Integer(tempIndex * 2 + 2));
}
tempTree = (BinaryCharTree) tempQueue.dequeue();
if (tempTree == null) {
break;
}
tempIndex = ((Integer) tempIntQueue.dequeue()).intValue();
}
}
24.二叉树的建立
在第22天的基础上增加了一种二叉树的构造方法,是相反的过程。先用一个线性表存储所有结点,再连接相应的结点,最后把线性表中第一个结点作为根结点。
核心代码:
遍历所有结点,如果i结点的下标等于j结点下标×2+1,则j为i的左结点,如果i结点的下标等于j结点下标×2+2,则j为i的右结点。
for (int i = 1; i < tempNumNodes; i++) {
for (int j = 0; j < i; j++) {
System.out.println("indices " + paraIndicesArray[j] + " vs. " + paraIndicesArray[i]);
if (paraIndicesArray[i] == paraIndicesArray[j] * 2 + 1) {
tempAllNodes[j].leftChild = tempAllNodes[i];
System.out.println("Linking " + j + " with " + i);
break;
} else if (paraIndicesArray[i] == paraIndicesArray[j] * 2 + 2) {
tempAllNodes[j].rightChild = tempAllNodes[i];
System.out.println("Linking " + j + " with " + i);
break;
}
}
}
25.二叉树深度遍历的栈实现 (中序)
25.1具有通用性的对象栈
修改第十四天栈的代码,将char改为通用的object。
int depth;
Object[] data;
public ObjectStack() {
depth = 0;
data = new Object[MAX_DEPTH];
}
增加了判断栈是否为空(栈深度为0时栈空)的代码:
public boolean isEmpty() {
if (depth == 0) {
return true;
}
return false;
}
用强制类型转换:
for (char ch = 'a'; ch < 'm'; ch++) {
tempStack.push(new Character(ch));
System.out.println("The current stack is: " + tempStack);
}
char tempChar;
for (int i = 0; i < 12; i++) {
tempChar = ((Character)tempStack.pop()).charValue();
System.out.println("Poped: " + tempChar);
System.out.println("The current stack is: " + tempStack);
}
25.2中序遍历
利用栈来实现二叉树的中序遍历:
如果栈不为空并且结点有数据,那么该结点入栈,访问该结点,然后访问他的左结点,直到已没有左结点(即tempNode==null时),将栈顶出栈(就是二叉树最左边的结点),输出结点的值,然后再访问该结点的右结点。
public void inOrderVisitWithStack() {
ObjectStack tempStack = new ObjectStack();
BinaryCharTree tempNode = this;
while (!tempStack.isEmpty() || tempNode != null) {
if (tempNode != null) {
tempStack.push(tempNode);
tempNode = tempNode.leftChild;
} else {
tempNode = (BinaryCharTree) tempStack.pop();
System.out.print("" + tempNode.value + " ");
tempNode = tempNode.rightChild;
}
}
}
二叉树:
char[] tempCharArray = { ‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’ };
int[] tempIndicesArray = { 0, 1, 2, 4, 5, 12 };
中序遍历结果为:B D A E F C
26.二叉树深度遍历的栈实现 (前序和后序)
前序遍历:
前序就是输出结点的语句位置和中序不一样,因为前序是先访问根结点,所以一访问结点就要输出,中序是先访问左结点,所以当访问到最左边的结点时再出栈输出结点。
前序遍历结果:A B D C E F
public void preOrderVisitWithStack() {
ObjectStack tempStack = new ObjectStack();
BinaryCharTree tempNode = this;
while (!tempStack.isEmpty() || tempNode != null) {
if (tempNode != null) {
System.out.print("" + tempNode.value + " ");
tempStack.push(tempNode);
tempNode = tempNode.leftChild;
} else {
tempNode = (BinaryCharTree) tempStack.pop();
tempNode = tempNode.rightChild;
}
}
}
后续遍历:
在二叉树中有六种遍历排列
1.左中右—中序
2.左右中—后序
3.中左右—前序
4.右中左
5.右左中
6.中右左
在前序遍历的基础上,将左右子树互换,便得到第6种排列,然后再将其倒序,就可以得到后序遍历了。
再申请一个栈来存储第六种排列,然后将其倒序输出
代码:与前序相同,只不过把输出语句换成将结点数据入栈。
后序遍历结果:D B F E C A
public void postOrderVisitWithStack() {
ObjectStack tempStack = new ObjectStack();
BinaryCharTree tempNode = this;
ObjectStack tempOutputStack = new ObjectStack();
while (!tempStack.isEmpty() || tempNode != null) {
if (tempNode != null) {
tempOutputStack.push(new Character(tempNode.value));
tempStack.push(tempNode);
tempNode = tempNode.rightChild;
} else {
tempNode = (BinaryCharTree) tempStack.pop();
tempNode = tempNode.leftChild;
}
}
while (!tempOutputStack.isEmpty()) {
System.out.print("" + tempOutputStack.pop() + " ");
}
}
27.Hanoi 塔问题
汉诺塔问题分析:
先用一个动图展示下汉诺塔的移动过程:
我们分情况讨论一下盘子的移动:
情况一:
当盘子只有1个(调用 hanoi(1,A,B,C))
盘子只有一个的时候,只要直接将盘子从开始柱移动到目标柱就可以了,并没有中间状态(即不用借助中转柱),在hanoi方法中可以用一句话表示该移动动作:
System.out.println(A -> C);
情况二:
当盘子有2个(调用 hanoi(A,B,C,2))这个情况分三个步骤进行:
step1. 把除了最大的盘子之外的盘子从A移到B
A—>B (开始柱—>中转柱) 【相当于调用 hanoi(A,C,B,1)】
step2. 把最大的盘子从A移到C
A—>C (开始柱—>目标柱) 【相当于调用 hanoi(A,B,C,1)】
step3. 把除了最大的盘子之外的盘子从B移到C
B—>C (中转柱—>目标柱) 【相当于调用 hanoi(B,A,C,1)】
情况三:
有3个盘子(调用 hanoi(A,B,C,3))
分三步进行:
step1. 把除了最大的盘子之外的盘子从A移到B(注意对于这个步骤来说此时A为开始柱,C为中转柱,B为目标柱,这样才能完成把最上面的2个盘子从A—>B的任务)
A—>C (开始柱—>中转柱) 【相当于调用 hanoi(A,B,C,1)】
A—>B (开始柱—>目标柱) 【相当于调用 hanoi(A,C,B,1)】
C—>B (中转柱—>目标柱) 【相当于调用 hanoi(C,A,B,1)】
step2. 把最大的盘子从A移到C(对于这个步骤来说此时A为开始柱,B为中转柱,C为目标柱,这样才能把最大的盘子从A—>C)
A—>C (开始柱—>目标柱) 【相当于调用 move(A,B,C,1),即直接执行 print(‘A—>C’)】
step3. 把除了最大的盘子之外的盘子从B移到C(注意对于这个步骤来说此时B为开始柱,A为中转柱,C为目标柱,这样才能完成把处于step2中的中转柱的2个盘子从B—>C的任务)
B —> A (开始柱—>中转柱) 【相当于调用 hanoi(B,C,A,1)】
B —> C (开始柱—>目标柱) 【相当于调用 hanoi(B,A,C,1)】
A —> C (中转柱—>目标柱) 【相当于调用 hanoi(A,B,C,1)】
情况三的step1和step3的形式和整个情况二的形式很像,要注意到分析的层次不相同时,开始柱,中转柱,目标柱是不一样的。对于step1来说中转柱是C,对于step3来说中转柱是A,对于整个情况三来说中转柱是B。
前面我们已经确定了情况二调用的函数是 hanoi(A,B,C,2),其等价于
A—>B
A—>C
B—>C
然后情况三的step1是
A—>C
A—>B
C—>B
跟情况二的形式是一样的,根据前面情况二的转化,那这三步就可以转化成函数 hanoi(A,C,B,2)
情况三的step3同理,做转化就成了函数 hanoi(B,A,C,2)
而情况三的step2可以直接用一句 println(A -> C) 来代替 hanoi(A,B,C,1)
所以整个情况三就可以这样来表示:
hanoi(A,C,B,2) //step1. 把除了最大的盘子之外的盘子从A移到B
println(A -> C) //step2. 把最大的盘子从A移到C
hanoi(B,A,C,2) //step3. 把除了最大的盘子之外的盘子从B移到C
以上三行代码又等价于函数 hanoi(A,B,C,3)
整体代码:
package xjx;
public class Hanoi {
public static void main(String args[]) {
hanoi('a', 'b', 'c', 3);
}
public static void hanoi(char paraSource, char paraIntermediary, char paraDestination, int paraNumber) {
if (paraNumber == 1) {
System.out.println(paraSource + "->" + paraDestination + " ");
return;
}
hanoi(paraSource, paraDestination, paraIntermediary, paraNumber - 1);
System.out.println(paraSource + "->" + paraDestination + " ");
hanoi(paraIntermediary, paraSource, paraDestination, paraNumber - 1);
}
}
移动过程:
28.Huffman 编码 (节点定义与文件读取)
哈夫曼树, 即带权路径最小的树, 权值最小的结点远离根结点, 权值越大的结点越靠近根结点。哈夫曼树也叫最优二叉树。
哈夫曼树编码:
左孩子路径编码为0,右孩子路径编码为1
如图:
即
A 的编码: 0
D 的编码: 10
B 的编码: 110
C 的编码: 111
如果考虑到进一步节省存储空间,就应该将出现概率大(占比多)的字符用尽量少的0-1进行编码,也就是更靠近根(节点少),这也就是最优二叉树—哈夫曼树。
权值大的在上层,权值小的在下层。满足出现频率高的码长短。
哈夫曼编码的带权路径权值:叶子节点的值 * 叶子节点的高度(根节点为0)
哈夫曼树的节点定义与文件读取:
package xjx;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.stream.Collectors;
public class Huffman {
//节点定义
class HuffmanNode {
char character;
int weight;
HuffmanNode leftChild;
HuffmanNode rightChild;
HuffmanNode parent;
public HuffmanNode(char paraCharacter, int paraWeight, HuffmanNode paraLeftChild,
HuffmanNode paraRightChild, HuffmanNode paraParent) {
character = paraCharacter;
weight = paraWeight;
leftChild = paraLeftChild;
rightChild = paraRightChild;
parent = paraParent;
}
public String toString() {
String resultString = "(" + character + ", " + weight + ")";
return resultString;
}
}
public static final int NUM_CHARS = 256;
//存储文件的字符串
String inputText;
//叶子节点数
int alphabetLength;
char[] alphabet;
//哈夫曼树总结点数 2*alphabetLength-1
int[] charCounts;
int[] charMapping;
//哈夫曼树结点
String[] huffmanCodes;
HuffmanNode[] nodes;
public Huffman(String paraFilename) {
charMapping = new int[NUM_CHARS];
readText(paraFilename);
}
//文件读取
public void readText(String paraFilename) {
try {
inputText = Files.newBufferedReader(Paths.get(paraFilename), StandardCharsets.UTF_8)
.lines().collect(Collectors.joining("\n"));
} catch (Exception ee) {
System.out.println(ee);
System.exit(0);
}
System.out.println("The text is:\r\n" + inputText);
}
}
29.Huffman 编码 (建树)
建立哈夫曼树是一个自底向上的过程,从最小的开始选择,最后生成的结点是根结点。
例如:将3、5、7、9这四个数字建立一个哈夫曼树,先选择3和5分别作为一个新结点的左孩子和右孩子,生成的新结点权值为8,然后选择7和8再作为一个新结点的左孩子和右孩子,新结点权值为15,最后选择9和15。
生成的哈夫曼树如图:
建立哈夫曼树过程的代码:
public void constructAlphabet() {
Arrays.fill(charMapping, -1);
int[] tempCharCounts = new int[NUM_CHARS];
int tempCharIndex;
//计算字符个数
char tempChar;
for (int i = 0; i < inputText.length(); i++) {
tempChar = inputText.charAt(i);
tempCharIndex = (int) tempChar;
System.out.print("" + tempCharIndex + " ");
tempCharCounts[tempCharIndex]++;
}
//计算叶子节点数
alphabetLength = 0;
for (int i = 0; i < 255; i++) {
if (tempCharCounts[i] > 0) {
alphabetLength++;
}
}
//将字符存入数组
alphabet = new char[alphabetLength];
charCounts = new int[2 * alphabetLength - 1];
int tempCounter = 0;
for (int i = 0; i < NUM_CHARS; i++) {
if (tempCharCounts[i] > 0) {
alphabet[tempCounter] = (char) i;
charCounts[tempCounter] = tempCharCounts[i];
charMapping[i] = tempCounter;
tempCounter++;
}
}
System.out.println("The alphabet is: " + Arrays.toString(alphabet));
System.out.println("Their counts are: " + Arrays.toString(charCounts));
System.out.println("The char mappings are: " + Arrays.toString(charMapping));
}
public void constructTree() {
//申请空间
nodes = new HuffmanNode[alphabetLength * 2 - 1];
boolean[] tempProcessed = new boolean[alphabetLength * 2 - 1];
//初始化叶子结点
for (int i = 0; i < alphabetLength; i++) {
nodes[i] = new HuffmanNode(alphabet[i], charCounts[i], null, null, null);
}
//建树
int tempLeft, tempRight, tempMinimal;
for (int i = alphabetLength; i < 2 * alphabetLength - 1; i++) {
//选择第一小的作为左孩子
tempLeft = -1;
tempMinimal = Integer.MAX_VALUE;
for (int j = 0; j < i; j++) {
if (tempProcessed[j]) {
continue;
}
if (tempMinimal > charCounts[j]) {
tempMinimal = charCounts[j];
tempLeft = j;
}
}
tempProcessed[tempLeft] = true;
//选择第二小的作为右孩子
tempRight = -1;
tempMinimal = Integer.MAX_VALUE;
for (int j = 0; j < i; j++) {
if (tempProcessed[j]) {
continue;
}
if (tempMinimal > charCounts[j]) {
tempMinimal = charCounts[j];
tempRight = j;
}
}
tempProcessed[tempRight] = true;
System.out.println("Selecting " + tempLeft + " and " + tempRight);
//创建新结点
charCounts[i] = charCounts[tempLeft] + charCounts[tempRight];
nodes[i] = new HuffmanNode('*', charCounts[i], nodes[tempLeft], nodes[tempRight], null);
//连接自己的左孩子、右孩子结点
nodes[tempLeft].parent = nodes[i];
nodes[tempRight].parent = nodes[i];
System.out.println("The children of " + i + " are " + tempLeft + " and " + tempRight);
}
}
public HuffmanNode getRoot() {
return nodes[nodes.length - 1];
}
30.Huffman 编码 (编码与解码)
接前两天的代码。
编码是从哈夫曼树的叶节点到根节点, 解码就是反过来。
编码在第28天已经讲到了,就是左子树分支为0右子树分支为1依此编码
解码就是输出对应的01串的的字符
public void preOrderVisit(HuffmanNode paraNode) {
System.out.print("(" + paraNode.character + ", " + paraNode.weight + ") ");
if (paraNode.leftChild != null) {
preOrderVisit(paraNode.leftChild);
}
if (paraNode.rightChild != null) {
preOrderVisit(paraNode.rightChild);
}
}
public void generateCodes() {
huffmanCodes = new String[alphabetLength];
HuffmanNode tempNode;
for (int i = 0; i < alphabetLength; i++) {
tempNode = nodes[i];
String tempCharCode = "";
while (tempNode.parent != null) {
if (tempNode == tempNode.parent.leftChild) {
tempCharCode = "0" + tempCharCode;
} else {
tempCharCode = "1" + tempCharCode;
}
tempNode = tempNode.parent;
}
huffmanCodes[i] = tempCharCode;
System.out.println("The code of " + alphabet[i] + " is " + tempCharCode);
}
}
public String coding(String paraString) {
String resultCodeString = "";
int tempIndex;
for (int i = 0; i < paraString.length(); i++) {
tempIndex = charMapping[(int) paraString.charAt(i)];
resultCodeString += huffmanCodes[tempIndex];
}
return resultCodeString;
}
public String decoding(String paraString) {
String resultCodeString = "";
HuffmanNode tempNode = getRoot();
for (int i = 0; i < paraString.length(); i++) {
if (paraString.charAt(i) == '0') {
tempNode = tempNode.leftChild;
System.out.println(tempNode);
} else {
tempNode = tempNode.rightChild;
System.out.println(tempNode);
}
if (tempNode.leftChild == null) {
System.out.println("Decode one:" + tempNode);
resultCodeString += tempNode.character;
tempNode = getRoot();
}
}
return resultCodeString;
}
public static void main(String args[]) {
Huffman tempHuffman = new Huffman("D:/");
tempHuffman.constructAlphabet();
tempHuffman.constructTree();
HuffmanNode tempRoot = tempHuffman.getRoot();
System.out.println("The root is: " + tempRoot);
System.out.println("Preorder visit:");
tempHuffman.preOrderVisit(tempHuffman.getRoot());
tempHuffman.generateCodes();
String tempCoded = tempHuffman.coding("abcdb");
System.out.println("Coded: " + tempCoded);
String tempDecoded = tempHuffman.decoding(tempCoded);
System.out.println("Decoded: " + tempDecoded);
}