1. 引子
当前素质教育的背景下,小学的学科成绩都改为了优秀、良好、及格、不及格这样的模糊词语,而不再通报具体的分数。
用代码可以表示如下:
if( a < 60 )
System.out.print("不及格");
else if( a < 70 )
System.out.print("及格");
else if( a < 90 )
System.out.print("良好");
else
System.out.print("优秀");
粗略看这样做是没问题的,但是一张好的试卷大部分学生成绩集中在"良好",而上面的程序需要判断两次才能到"良好",输入量很大的时候,在算法效率上其实是有问题的。所以应该做如下改进(① -> ②):

2. 哈夫曼树定义和原理
我们先把上图简化成叶子结点带权的二叉树(注:树结点间的连线相关的数叫做权,Weight)。

① 结点的路径长度:从根结点到该结点的路径上的连接数。
②
树的路径长度:树中每个叶子结点的路径长度之和。
③
结点带权路径长度:结点的路径长度与结点权值的乘积。
④
树的带权路径长度:WPL(Weighted Path Length)是树中所有叶子结点的带权路径长度之和。
※WPL的值越小,说明构造出来的二叉树性能越优,这种最优二叉树又称为哈夫曼树。
3. 哈夫曼树的存储结构:
weight
|
data
|
leftChild
|
rightChild
|
哈夫曼树的结点存储结构为双亲孩子存储结构:
① weight: 结点的权值。
② data:结点的值。
③ leftChild:结点的左孩子。
④ rightChild:结点的右孩子。
4. 构造哈夫曼树:
对于已知的一组叶子的权值W 1 ,W 2...... ,W n
① 首先把 n 个叶子结点看做 n 棵树(仅有一个结点的二叉树),n棵树组成一个森林。
② 把森林中权值最小和次小的两棵树合并成一棵树(小的放左边,大的放右边),该树根结点的权值是两棵子树权值之和,这时森林中还有 n-1 棵树。
③ 重复第②步直到森林中只有一棵为止。此树就是哈夫曼树。
现给一组 (n=4) 具体的权值 2 、4 、5 、 8 ,下边是构造具体过程:

5. Java实现哈夫曼树
import java.util.*;
public class HuffmanTree {
/*
* 结点内部类
*/
public static class Node {
private String data; // 结点值
private int weight; // 权值
private Node leftChild; // 左孩子
private Node rightChild; // 右孩子
public Node(String data, int weight) {
this.data = data;
this.weight = weight;
}
public String toString() {
return "Node[data=" + data + ", weight=" + weight + "]";
}
}
/**
* 构造哈夫曼树
*
* @param nodes节点集合
* @return 构造出来的哈夫曼树的根节点
*/
private static Node buildHaffmanTree(List<Node> nodesList) {
while (nodesList.size() > 1) { // 只要nodes数组中还有2个以上的节点
quickSort(nodesList, 0, nodesList.size() - 1);
Node left = nodesList.get(nodesList.size() - 1); // 获取权值最小的两个节点
Node right = nodesList.get(nodesList.size() - 2);
Node parent = new Node(null, left.weight + right.weight); // 生成新节点,新节点的权值为两个子节点的权值之和
parent.leftChild = left; // 让新节点作为权值最小的两个节点的父节点
parent.rightChild = right;
nodesList.remove(nodesList.size() - 1); // 删除权值最小的两个节点
nodesList.remove(nodesList.size() - 1);
nodesList.add(parent); // 将新生成的父节点添加到集合中
}
return nodesList.get(0); // 返回nodes集合中唯一的节点,也就是根节点
}
/**
* 实现快速排序算法,用于对节点进行排序
*
* @param nodesList
* @param start
* @param end
*/
private static void quickSort(List<Node> nodesList, int start, int end) {
if (start < end) { // 需要排序
Node base = nodesList.get(start); // 以第一个元素作为分界值
int i = start; // i从左边搜索,搜索大于分界值的元素的索引
int j = end + 1; // j从右边开始搜索,搜索小于分界值的元素的索引
while (true) {
while (i < end && nodesList.get(++i).weight >= base.weight){ // 找到大于分界值的元素的索引,或i已经到了end处
;
}
while (j > start && nodesList.get(--j).weight <= base.weight){ // 找到小于分界值的元素的索引,或j已经到了start处
;
}
if (i < j) {
swap(nodesList, i, j);
} else {
break;
}
}
swap(nodesList, start, j);
quickSort(nodesList, start, j - 1); // 递归左子序列
quickSort(nodesList, j + 1, end); // 递归右边子序列
}
}
/**
* 将指定数组的i和j索引处的元素交换
*
* @param nodesList
* @param i
* @param j
*/
private static void swap(List<Node> nodesList, int i, int j) {
Node temp;
temp = nodesList.get(i);
nodesList.set(i, nodesList.get(j));
nodesList.set(j, temp);
}
/**
* 广度优先遍历
*
* @param root
* @return
*/
public static List<Node> breadthFirst(Node root) {
Queue<Node> queue = new ArrayDeque<Node>();
List<Node> list = new ArrayList<Node>();
if (root != null) {
queue.offer(root); // 将根元素入"队列"
}
while (!queue.isEmpty()) {
list.add(queue.peek()); // 将该队列的"队尾"的元素添加到List中
Node p = queue.poll();
if (p.leftChild != null) { // 如果左子节点不为null,将它加入"队列"
queue.offer(p.leftChild);
}
if (p.rightChild != null) { // 如果右子节点不为null,将它加入"队列"
queue.offer(p.rightChild);
}
}
return list;
}
/**
* 测试方法
*
* @param args
*/
public static void main(String[] args) {
List<Node> nodesList = new ArrayList<Node>();
nodesList.add(new Node("A", 2));
nodesList.add(new Node("B", 4));
nodesList.add(new Node("C", 5));
nodesList.add(new Node("D", 8));
Node root = HuffmanTree.buildHaffmanTree(nodesList);
System.out.println(breadthFirst(root));
}
}
分享到:
相关推荐
哈夫曼编码是一种数据压缩方法,它利用了字符出现频率的不同来构建一棵特殊的二叉树——哈夫曼树。在哈夫曼树中,频率较高的字符对应的路径较短,而频率较低的字符路径较长,这样可以使得频繁出现的字符在编码时使用...
哈夫曼树的应用——数据结构课程设计 哈夫曼树是一种特殊的二叉树,它的每个叶子节点都对应一个权值,树的构建是根据权值的大小进行排序的。哈夫曼树的应用非常广泛,例如数据压缩、编码、解码等。 在数据结构课程...
5. **解码**:有了哈夫曼编码,可以将原始数据按照编码规则进行压缩,然后在接收端通过同样的哈夫曼树结构进行解码,恢复原始数据。 在这个VC++6.0的项目中,程序可能包括以下部分: - 输入处理:读取字符频率表或...
任务 :建立建立最优二叉树函数 要求:可以建立函数输入...在上交资料中请写明:存储结构、 基本算法(可以使用程序流程图) 、输入输出、源程序、测试数据和结果、算法的时间复杂度、另外可以提出算法的改进方法;
数据结构课程设计的目标是让学生能够灵活运用所学的数据结构知识,特别是哈夫曼树这一重要概念,来解决实际问题。哈夫曼树是一种特殊的二叉树,常用于数据压缩和编码,通过构建最小带权路径长度的二叉树,使得频率高...
最优二叉树,又称为哈夫曼树或最小带权路径长度树,是一种特殊的二叉树,主要用于数据编码,特别是数据压缩。在最优二叉树中,每个叶子节点都代表一个待编码的字符,而其权重是该字符在数据源中的出现频率。非叶子...
2. 实现哈夫曼树数据结构,使用哈夫曼树完成如下文档的编码与译码,假设该文档由5种符号字符(A、B、C、D、E)构成 ABACDEABBCEABAACCCDEACCBAABCCCA 3. 选做:实现二叉树的中序遍历线索化数据结构 4. 选做:使用...
哈夫曼树是一种特殊的二叉树,它的每个节点的权重是其所有子节点的权重之和。哈夫曼树的应用非常广泛,如数据压缩、编码、译码等。 哈夫曼树的存储结构 哈夫曼树的存储结构可以使用静态三叉链表来实现。每个节点...
哈夫曼树是数据结构和算法中的一种重要概念,它是一种构建最优二叉树的方法,可以用来压缩数据和编码。下面是对哈夫曼树的知识点的详细解释。 哈夫曼树的定义 哈夫曼树是一种特殊的二叉树,它的每个结点都有一个...
它的核心思想是通过构建一种特殊的二叉树——最优二叉树(也称为最小带权路径长度树),来实现对数据的编码。这种树的特点是:频率高的字符对应的路径较短,而频率低的字符对应的路径较长,从而使得编码后的平均码长...
哈夫曼树是一种特殊的二叉树,用于解决数据编码和压缩问题,特别是在数据通信和文件压缩领域广泛应用。哈夫曼编码是一种高效的前缀编码方法,它根据数据出现频率分配最短的二进制码,频繁出现的字符拥有较短的编码,...
- 打印哈夫曼树:可视化显示哈夫曼树结构,帮助理解编码过程。 - 退出程序:结束程序运行。 **总体设计** 1. **系统框图**:通常包括输入模块、编码模块、存储模块、解码模块和输出模块。输入模块接收文本,编码...
定义:给定n个权值作为n个叶子结点,构造一棵二叉树,若树的带权路径长度达到最小,则这棵树被称为哈夫曼树。
最优二叉树,也称为哈夫曼树或最小带权路径长度树,是数据结构和算法领域中的一个重要概念。在信息编码中,特别是在数据压缩中,哈夫曼编码是一种非常有效的无损数据压缩方法。本课程设计作业主要探讨了如何构建和...
在计算机科学中,哈夫曼树(Huffman Tree),又称最优二叉树,是一种特殊的二叉树结构,它在数据编码,特别是数据压缩领域中有着广泛的应用。哈夫曼树通过构建最小带权路径长度(Minimum Weighted Path Length, MWPL...
内容概要:本文档详细介绍了一种基于数据结构中的哈夫曼树实现字符频率统计、哈夫曼编码和译码的具体步骤。首先介绍哈夫曼树的基本概念和原理,接着讲解如何通过C++程序读取文件,提取出文本中各个字符出现的概率,...
哈夫曼树(Huffman Tree),也称为最优二叉树,是数据压缩领域中的一个重要概念。它是基于贪心策略的一种数据结构,用于构建一种特殊的二叉树,使得带权路径长度最短,从而达到编码效率最高。哈夫曼树的核心思想是...
在数据结构的学习中,哈夫曼树(Huffman Tree)是一种特殊的二叉树,它在数据编码、数据压缩等领域有着广泛的应用。哈夫曼树基于贪心算法构建,旨在通过最小化路径长度来优化带权路径长度,从而提高数据传输或存储的...
数据结构作业:第六章二叉树作业 本资源摘要信息主要关注数据结构的二叉树章节,涵盖了二叉树的基本概念、性质、操作和应用。该资源提供了多种题型,包括判断正误、填空、选择题、阅读分析题和算法设计题,旨在考察...