O(∩_∩)O~~

概述

我想学过数据结构的小伙伴一定都认识哈弗曼,这位大神发明了大名鼎鼎的“最优二叉树”,为了纪念他呢,我们称之为“哈弗曼树”。哈弗曼树可以用于哈弗曼编码,编码的话学问可就大了,比如用于压缩,用于密码学等。今天一起来看看哈弗曼树到底是什么东东。

概念

当然,套路之一,首先我们要了解一些基本概念。

1.              路径长度:从树中的一个结点到另一个结点之间的分支构成这两个结点的路径,路径上的分支数目称为路径长度。

2.              树的路径长度:从树根到每一个结点的路径长度之和,我们所说的完全二叉树就是这种路径长度最短的二叉树。

3.              树的带权路径长度:如果在树的每一个叶子结点上赋上一个权值,那么树的带权路径长度就等于根结点到所有叶子结点的路径长度与叶子结点权值乘积的总和。

那么我们怎么判断一棵树是否为最优二叉树呢,先看看下面几棵树:

他们的带权长度分别为:

WPL1:7*2+5*2+2*2+4*2=36

WPL2:7*3+5*3+2*1+4*2=46

WPL3:7*1+5*2+2*3+4*3=35

很明显,第三棵树的带权路径最短(不信的小伙伴可以试一试,要是能找到更短的,估计能拿图灵奖了),这就是我们所说的“最优二叉树(哈弗曼树)”,它的构建方法很简单,依次选取权值最小的结点放在树的底部,将最小的两个连接构成一个新结点,需要注意的是构成的新结点的权值应该等于这两个结点的权值之和,然后要把这个新结点放回我们需要构成树的结点中继续进行排序,这样构成的哈弗曼树,所有的存储有信息的结点都在叶子结点上。

概念讲完,可能有点小伙伴还是“不明觉厉”。下面举个例子构建一下就清楚了。

有一个字符串:aaaaaaaaaabbbbbaaaaaccccccccddddddfff

第一步,我们先统计各个字符出现的次数,称之为该字符的权值。a 15 ,b 5, c 8, d 6, f 3。

第二步,找去这里面权值最小的两个字符,b5和f3,构建节点。

然后将f3和b5去掉,现在是a15,c8,d6,fb8。

第三步,重复第二步,直到构建出只剩一个节点。

现在是dfb14,a15,c8。

最后,

ok,这样我们的哈弗曼树就构造完成了。

构建的步骤

按照上面的逻辑,总结起来,就是一下几个步骤:

1.统计字符串中字符以及字符的出现次数;

2.根据第一步的结构,创建节点;

3.对节点权值升序排序;

4.取出权值最小的两个节点,生成一个新的父节点;

5.删除权值最小的两个节点,将父节点存放到列表中;

6.重复第四五步,直到剩下一个节点;

7.将最后的一个节点赋给根节点。

java代码

原理说完了,接下来是代码实现了。

首先需要有个节点类来存放数据。

 package huffman;
/**
* 节点类
* @author yuxiu
*
*/
public class Node {
public String code;// 节点的哈夫曼编码
public int codeSize;// 节点哈夫曼编码的长度
public String data;// 节点的数据
public int count;// 节点的权值
public Node lChild;
public Node rChild; public Node() {
} public Node(String data, int count) {
this.data = data;
this.count = count;
} public Node(int count, Node lChild, Node rChild) {
this.count = count;
this.lChild = lChild;
this.rChild = rChild;
} public Node(String data, int count, Node lChild, Node rChild) {
this.data = data;
this.count = count;
this.lChild = lChild;
this.rChild = rChild;
}
}

然后就是实现的过程了。

 package huffman;

 import java.io.*;
import java.util.*; public class Huffman {
private String str;// 最初用于压缩的字符串
private String newStr = "";// 哈夫曼编码连接成的字符串
private Node root;// 哈夫曼二叉树的根节点
private boolean flag;// 最新的字符是否已经存在的标签
private ArrayList<String> charList;// 存储不同字符的队列 相同字符存在同一位置
private ArrayList<Node> NodeList;// 存储节点的队列 /**
* 构建哈夫曼树
*
* @param str
*/
public void creatHfmTree(String str) {
this.str = str;
charList = new ArrayList<String>();
NodeList = new ArrayList<Node>();
// 1.统计字符串中字符以及字符的出现次数
// 基本思想是将一段无序的字符串如ababccdebed放到charList里,分别为aa,bbb,cc,dd,ee
// 并且列表中字符串的长度就是对应的权值
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i); // 从给定的字符串中取出字符
flag = true;
for (int j = 0; j < charList.size(); j++) {
if (charList.get(j).charAt(0) == ch) {// 如果找到了同一字符
String s = charList.get(j) + ch;
charList.set(j, s);
flag = false;
break;
}
}
if (flag) {
charList.add(charList.size(), ch + "");
}
}
// 2.根据第一步的结构,创建节点
for (int i = 0; i < charList.size(); i++) {
String data = charList.get(i).charAt(0) + ""; // 获取charList中每段字符串的首个字符
int count = charList.get(i).length(); // 列表中字符串的长度就是对应的权值
Node node = new Node(data, count); // 创建节点对象
NodeList.add(i, node); // 加入到节点队列
} // 3.对节点权值升序排序
Sort(NodeList);
while (NodeList.size() > 1) {// 当节点数目大于一时
// 4.取出权值最小的两个节点,生成一个新的父节点
// 5.删除权值最小的两个节点,将父节点存放到列表中
Node left = NodeList.remove(0);
Node right = NodeList.remove(0);
int parentWeight = left.count + right.count;// 父节点权值等于子节点权值之和
Node parent = new Node(parentWeight, left, right);
NodeList.add(0, parent); // 将父节点置于首位 }
// 6.重复第四五步,就是那个while循环
// 7.将最后的一个节点赋给根节点
root = NodeList.get(0);
}
/**
* 升序排序
*
* @param nodelist
*/
public void Sort(ArrayList<Node> nodelist) {
for (int i = 0; i < nodelist.size() - 1; i++) {
for (int j = i + 1; j < nodelist.size(); j++) {
Node temp;
if (nodelist.get(i).count > nodelist.get(j).count) {
temp = nodelist.get(i);
nodelist.set(i, nodelist.get(j));
nodelist.set(j, temp);
} }
} } /**
* 遍历
*
* @param node
* 节点
*/
public void output(Node node) {
if (node.lChild != null) {
output(node.lChild);
}
System.out.print(node.count + " "); // 中序遍历
if (node.rChild != null) {
output(node.rChild);
}
} public void output() {
output(root);
}
/**
* 主方法
*
* @param args
*/
public static void main(String[] args) {
Huffman huff = new Huffman();//创建哈弗曼对象
huff.creatHfmTree("sdfassvvdfgsfdfsdfs");//构造树
}

java实现哈弗曼树的更多相关文章

  1. java实现哈弗曼树和哈夫曼树压缩

    本篇博文将介绍什么是哈夫曼树,并且如何在java语言中构建一棵哈夫曼树,怎么利用哈夫曼树实现对文件的压缩和解压.首先,先来了解下什么哈夫曼树. 一.哈夫曼树 哈夫曼树属于二叉树,即树的结点最多拥有2个 ...

  2. 哈弗曼树的理解和实现(Java)

    哈弗曼树概述 哈弗曼树又称最优树,是一种带权路径长度最短的树,在实际中有广泛的用途.哈弗曼树的定义,涉及路径.路径长度.权等概念.哈弗曼树可以用于哈弗曼编码,用于压缩,用于密码学等. 哈弗曼树的一些定 ...

  3. K:哈弗曼树

    相关介绍:  树形结构除了应用于查找和排序等操作时能调高效率,它在信息通讯领域也有着广泛的应用.哈弗曼(Huffman)树就是一种在编码技术方面得到广泛应用的二叉树,它同时也是一种最优二叉树. 哈弗曼 ...

  4. HDU2527:Safe Or Unsafe(哈弗曼树)

    Problem Description Javac++ 一天在看计算机的书籍的时候,看到了一个有趣的东西!每一串字符都可以被编码成一些数字来储存信息,但是不同的编码方式得到的储存空间是不一样的!并且当 ...

  5. SLT 优先队列 哈弗曼树最小带权路径

    与普通的队列不同,普通的队列是先进先出的,而优先队列出队的顺序不是先进先出,而是大(或者小)元素先出队,需要#include <queue> 成员函数 成员函数 作用 empty() 判断 ...

  6. PKU 1521 Entropy(简单哈弗曼树_水过)

    题目大意:原题链接 给你一个字符串,首先是计算出一个按正常编码的编码长度,其次是计算出一个用霍夫曼编码的编码长度,最后求正常编码的长度除以霍夫曼编码长度的比值,保留一位小数. 解题思路:需要知道 1. ...

  7. POJ 3253 Fence Repair(简单哈弗曼树_水过)

    题目大意:原题链接 锯木板,锯木板的长度就是花费.比如你要锯成长度为8 5 8的木板,最简单的方式是把21的木板割成13,8,花费21,再把13割成5,8,花费13,共计34,当然也可以先割成16,5 ...

  8. Python 数据结构与算法 —— 哈弗曼树

    1. 从扩充二叉树到哈弗曼树 扩充二叉树:对二叉树 T,加入足够多的新叶节点(而不是任意),使 T 的原有结点都变成度数为 2 的分支节点,得到的二叉树称为 T 的扩充二叉树. 对于扩充二叉树而言, ...

  9. POJ 3253 Fence Repair【哈弗曼树/贪心/优先队列】

    Fence Repair Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 53645   Accepted: 17670 De ...

随机推荐

  1. 在 vSphere 5.x/6.0 中配置 Network Dump Collector 服务 (2002954)

    vmware KB: https://kb.vmware.com/s/article/2002954?lang=zh_CN 重点配置命令: 使用 vSphere Client 连接到 vCenter ...

  2. 详解权限管理(RBAC)的实现方法

    在说权限管理前,应该先知道权限管理要有哪些功能: ().用户只能访问,指定的控制器,指定的方法 ().用户可以存在于多个用户组里 ().用户组可以选择,指定的控制器,指定的方法 ().可以添加控制器和 ...

  3. 第八章 计时器(DIGCLOCK)

    /*-------------------------------------- DIGCLOCK.C -- Digital Clock (c) Charles Petzold, 1998 ----- ...

  4. 设计模式之——单例模式(Singleton)的常见应用场景(转):

    单例模式(Singleton)也叫单态模式,是设计模式中最为简单的一种模式,甚至有些模式大师都不称其为模式,称其为一种实现技巧,因为设计模式讲究对象之间的关系的抽象,而单例模式只有自己一个对象,也因此 ...

  5. ZT Android4.2蓝牙基础架构学习

    Android4.2蓝牙基础架构学习 分类: Jellybean Bluetooth Bluetooth 2013-10-13 23:58 863人阅读 评论(3) 收藏 举报 androidblue ...

  6. 18年10月30日 NOIP模拟赛

    T1 jkl 题解 显然每次都取a[i]的最大值/最小值,并更新a[i]即可 用数据结构维护这一操作..得分看常数 事实上用v[i]记录权值为i的个数,然后for乱搞就可以了... 其它乱搞做法能获得 ...

  7. 使用 CSS 根据兄弟元素的个数来调整样式

    在某些场景下,我们需要根据兄弟元素的总数来为它们设置样式.最常见的场景就是,当一个列表不断延长时,通过隐藏控件或压缩控件等方式来节省屏幕空间,以此提升用户体验. 为保证一屏内容能展示更多的内容,需要将 ...

  8. 1040. [ZJOI2008]骑士【树形DP】

    Description Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社会各 界的赞扬.最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争.战 ...

  9. 【CF809E】Surprise me!

    题目 这是一道神仙题 看到这样一个鬼畜的柿子 \[\sum_{i=1}^n\sum_{j=1}^n\varphi(a_i\times a_j)\times dis(i,j)\] 又是树上距离又是\(\ ...

  10. B. Our Tanya is Crying Out Loud

    http://codeforces.com/problemset/problem/940/B Right now she actually isn't. But she will be, if you ...