哈夫曼树的概念,参考博客哈夫曼树
哈夫曼树,也就是带权路径长度最小的二叉树,最优二叉树。
构造一个最优二叉树,每次都取最小的两个元素构成树。
具体代码实现:
1、哈夫曼结点类
/**
* 哈夫曼节点类
*
* @author kiki
*
*/
public class HaffNode {
int weight; // 权值
int flag; // 标志(是否叶子节点)
int parent; // 双亲节点下标
int leftChild; // 左孩子下标
int rightChild; // 右孩子下标
public HaffNode() {
}
@Override
public String toString() {
return "HaffNode [weight=" + weight + ", flag=" + flag + ", parent=" + parent + ", leftChild=" + leftChild
+ ", rightChild=" + rightChild + "]";
}
}
2、哈夫曼编码类
示例数组[1, 3, 5, 7]构成哈夫曼树
哈夫曼编码,从根节点到叶子结点,左结点为0,右结点为1;
数组哈夫曼编码分别为:0100, 0101, 0011, 0000
import java.util.Arrays;
/**
* 哈夫曼编码
*
* @author kiki 2018年11月26日
*/
public class HaffmanCode {
int[] bit; // 编码数组
int start; // 编码起始下标
int weight; // 字符权值
public HaffmanCode(int n) {
bit = new int[n];
start = n - 1;
}
@Override
public String toString() {
return "HaffmanCode [bit=" + Arrays.toString(bit) + ", start=" + start + ", weight=" + weight + "]";
}
}
3、哈夫曼树构建类
/**
* 哈夫曼树和哈夫曼编码
* @author kiki 2018年11月27日
*/
public class HaffmanTree {
static final int MAX_VALUE = 1000; // 最大权值
static int nodeNum; // 叶子结点数量
static int totalNum; // 结点总数
public HaffmanTree(int n) {
nodeNum = n;
totalNum = 2 * n - 1;
}
public static void main(String[] args) {
int[] values = { 1, 3, 5, 7 }; // 叶子节点
int n = values.length;
HaffmanTree haffmanTree = new HaffmanTree(n);
// 初始化哈夫曼树结点
HaffNode[] nodes = new HaffNode[totalNum];
haffmanTree.buildHaffmanTree(values, nodes);
// 初始化哈夫曼编码
HaffmanCode[] codes = new HaffmanCode[nodeNum];
haffmanTree.buildHaffmanCode(nodes, codes);
for (int i = 0; i < totalNum; i++) {
System.out.println("weight: " + nodes[i]);
if (i < nodeNum) {
System.out.print(" | code: " + codes[i]);
}
System.out.println();
}
}
/**
* 构建哈夫曼树
*
* @param values
* @param nodes
*/
private void buildHaffmanTree(int[] values, HaffNode[] nodes) {
// 初始化结点数组
for (int i = 0; i < totalNum; i++) {
HaffNode node = new HaffNode();
if (i < nodeNum) { // 叶子节点
node.weight = values[i];
} else {
node.weight = 0;
}
node.flag = 0;
node.parent = -1;
node.leftChild = -1;
node.rightChild = -1;
nodes[i] = node;
}
// 遍历数组,比较出最小的两个结点,构成树
/*
* i=0;j<4 i=1;j<5 i=2;j<6 i=3;j<7
*/
// 非叶子结点数量nodeNum-1
// 构造非叶子结点过程
for (int i = 0; i < nodeNum - 1; i++) {
int value1 = MAX_VALUE;// 最小树权值
int value2 = MAX_VALUE;// 次小树权值
int index1 = 0; // 最小树索引
int index2 = 0; // 次小树索引
int tempTotalNum = nodeNum + i;
for (int j = 0; j < tempTotalNum; j++) {
if (nodes[j].weight < value1 && nodes[j].flag == 0) {
// 如果匹配值比最小结点小,将当前最小树变为次小树
value2 = value1;
index2 = index1;
// 将匹配值变为最小树
value1 = nodes[j].weight;
index1 = j;
} else if (nodes[j].weight < value2 && nodes[j].flag == 0) {
value2 = nodes[j].weight;
index2 = j;
}
}
// 父结点权值为子结点权值加和
nodes[tempTotalNum].weight = nodes[index1].weight + nodes[index2].weight;
nodes[tempTotalNum].leftChild = index1;
nodes[tempTotalNum].rightChild = index2;
// 最小树结点
nodes[index1].flag = 1; // 已标记
nodes[index1].parent = tempTotalNum;
// 次小树结点
nodes[index2].flag = 1; // 已标记
nodes[index2].parent = tempTotalNum;
}
}
/**
* 构建哈夫曼编码
*
* @param nodes
* @param codes
*/
private void buildHaffmanCode(HaffNode[] nodes, HaffmanCode[] codes) {
// 由哈夫曼树构造哈夫曼编码
int n = nodeNum;
HaffmanCode code = new HaffmanCode(n);
int child, parent;
// 求n个叶结点的哈夫曼编码
for (int i = 0; i < n - 1; i++) {
code.start = n - 1; // 不等长编码的最后一位为n-1
code.weight = nodes[i].weight;// 取得编码对应的权值
child = i;
parent = nodes[child].parent;
while (parent != -1) {
// 由叶结点向上直到根结点循环
if (nodes[parent].leftChild == child) {
code.bit[code.start] = 0;// 左孩子结点编码为0
} else {
code.bit[code.start] = 1;// 右孩子结点编码1
}
code.start--;
child = parent;
parent = nodes[child].parent;
}
HaffmanCode temp = new HaffmanCode(n);
// 保存叶结点的编码和不等长编码的起始位
for (int j = code.start + 1; j < n; j++) {
temp.bit[j] = code.bit[j];
}
temp.start = code.start;
temp.weight = code.weight;
codes[i] = temp;
}
}
}
运行结果:
HaffNode [weight=1, flag=1, parent=4, leftChild=-1, rightChild=-1]
HaffmanCode [bit=[0, 1, 0, 0], start=0, weight=1]
HaffNode [weight=3, flag=1, parent=4, leftChild=-1, rightChild=-1]
HaffmanCode [bit=[0, 1, 0, 1], start=0, weight=3]
HaffNode [weight=5, flag=1, parent=5, leftChild=-1, rightChild=-1]
HaffmanCode [bit=[0, 0, 1, 1], start=1, weight=5]
HaffNode [weight=7, flag=1, parent=6, leftChild=-1, rightChild=-1]
null
HaffNode [weight=4, flag=1, parent=5, leftChild=0, rightChild=1]
HaffNode [weight=9, flag=1, parent=6, leftChild=4, rightChild=2]
HaffNode [weight=16, flag=0, parent=-1, leftChild=3, rightChild=5]