数据结构与算法(Java)之哈夫曼树及其应用
Posted 达少Rising
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构与算法(Java)之哈夫曼树及其应用相关的知识,希望对你有一定的参考价值。
哈夫曼树
package com.weeks.tree.huffmantree;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author 达少
* @version 1.0
*
* 实现哈夫曼树(就是权值路径长度wpl最小的树)
*
*/
public class HuffmanTree {
public static void main(String[] args) {
int[] arr = {13, 7, 8, 3, 29, 6, 1};
Node root = createHuffmanTree(arr);
preOrder(root);
}
//构建哈夫曼树
public static Node createHuffmanTree(int[] arr){
//创建List对象,存储构建哈夫曼树的二叉树
List<Node> nodes = new ArrayList<>();
//根据数组的值,构建哈夫曼树的结点,并将结点放到List中
for (int i = 0; i < arr.length; i++) {
nodes.add(new Node(arr[i]));
}
while(nodes.size() > 1) {//当nodes中只剩下一个元素时才完成哈夫曼树的创建
//将nodes中的node按照升序排序
Collections.sort(nodes);
//取出nodes中俩棵最小的二叉树
Node leftNode = nodes.get(0);
Node rightNode = nodes.get(1);
//将取出的两棵二叉树作为新创建的结点的左右子树
Node parent = new Node(leftNode.getValue() + rightNode.getValue());
parent.setLeft(leftNode);
parent.setRight(rightNode);
//将取出的两棵二叉树从nodes中删除
nodes.remove(leftNode);
nodes.remove(rightNode);
//将新创建的二叉树加入到nodes中
nodes.add(parent);
}
return nodes.get(0);
}
//定义前序遍历方法,遍历哈夫曼树
public static void preOrder(Node root){
//判断书是否为空
if(root == null){
System.out.println("空树不能遍历!!!");
}
//遍历
root.preOrder();
}
}
//创建哈夫曼树结点(每一个单独的结点可以看作是最简单的一棵二叉树)
//因为建立哈夫曼树的过程中要比较结点的权值大小,所以为了方便比较要实现Comparable
//重写compareTo方法
class Node implements Comparable<Node> {
private int value;
private Node left;
private Node right;
public Node(int value){
this.value = value;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public Node getLeft() {
return left;
}
public void setLeft(Node left) {
this.left = left;
}
public Node getRight() {
return right;
}
public void setRight(Node right) {
this.right = right;
}
@Override
public String toString() {
return "Node{" +
"value=" + value +
'}';
}
@Override
public int compareTo(Node o) {
return this.value - o.value;
}
//前序遍历
public void preOrder(){
//判断当前结点是否为空
if(this == null){
return;
}
//先输出当前结点
System.out.println(this);
//递归遍历左子树
if(this.left != null){
this.left.preOrder();
}
//递归遍历右子树
if(this.right != null){
this.right.preOrder();
}
}
}
哈夫曼编码
package com.weeks.tree.huffmancode;
import java.util.*;
/**
* @author 达少
* @version 1.0
*
* 哈夫曼编码:
* 以编码字符串"i like like like java do you like a java"为例
* 1.找到每个字符出现的次数:d-1,y-1,u-1,v-2,o-2,i-4,l-4,k-4,i-5,a-5,空格-9
* 2.将字符出现的次数作为权值构建哈夫曼树
* 3.构建好哈夫曼树后,按照左子树为0,右子树为1,从根节点开始找到代表每个字符的路径就是
* 哈夫曼编码
*
*/
public class HuffmanCode {
public static void main(String[] args) {
String content = "i like like like java do you like a java";
List<Node> nodes = getNodes(content);
Node root = createHuffmanTree(nodes);
root.preOrder();
getCodes(root);
System.out.println(huffmanCodes);
}
//获得构建哈夫曼树的结点
public static List<Node> getNodes(String str){
//将字符串转化字节数组
byte[] bytes = str.getBytes();
//创建存储节点的List
List<Node> nodes = new ArrayList<>();
HashMap<Byte, Integer> map = new HashMap<>();
//统计每个字符出现的次数
for(byte b : bytes){
Integer count = map.get(b);
if(count == null){
map.put(b, 1);
}else{
map.put(b, count + 1);
}
}
//构建二叉树的结点
for (Map.Entry<Byte, Integer> entry: map.entrySet()){
nodes.add(new Node(entry.getKey(), entry.getValue()));
}
return nodes;
}
//构建哈夫曼树
public static Node createHuffmanTree(List<Node> nodes){
while(nodes.size() > 1){
//排序
Collections.sort(nodes);
//取出权值最小和次小的二叉树
Node leftNode = nodes.get(0);
Node rightNode = nodes.get(1);
//构建新的二叉树,权值为取出两二叉树之和
Node parent = new Node(null, leftNode.getCount() + rightNode.getCount());
//设置新创建的二叉树左右指针域,不设置则在遍历时无法遍历
parent.setLeft(leftNode);
parent.setRight(rightNode);
//将取出的二叉树冲nodes冲删除
nodes.remove(leftNode);
nodes.remove(rightNode);
//将新创建的二叉树加入nodes中
nodes.add(parent);
}
return nodes.get(0);//返回根结点
}
//前序遍历
public void preOrder(Node root){
if(root == null){
System.out.println("空树无法遍历");
}else{
root.preOrder();
}
}
//生成的哈夫曼编码应该存储在一个map对象中,结构是Byte(字符):String(字符对应的哈夫曼编码)
//所以定义一个Map属性用于存储字符对应的哈夫曼编码
static Map<Byte, String> huffmanCodes = new HashMap<>();
//在生成哈夫曼编码的过程中需要不断拼接,所以创建属性用于拼接哈夫曼编码
static StringBuilder stringBuilder = new StringBuilder();
//生成哈夫曼编码表
/**
* 功能是生成对应字符的哈夫曼编码
* @param node 哈夫曼树的结点
* @param code node对应的哈夫曼编码:如果是根结点就为空"", 左子结点为"0",右子结点为"1"
* @param sb 传入的StringBuilder用于存储拼接的哈夫曼编码
* @return
*/
public static void getCodes(Node node, String code, StringBuilder sb){
//新建一个StringBuilder临时存放拼接的哈夫曼编码
StringBuilder stringBuilder2 = new StringBuilder(sb);
//将当前的code加入stringBuilder2中
stringBuilder2.append(code);
if(node != null){//判断当前结点是否为空,如果不为空才能处理
//判断当前结点是否为叶子结点
if(node.getContent() == null){//非叶子结点
//向左递归
getCodes(node.getLeft(), "0", stringBuilder2);
//向右递归
getCodes(node.getRight(), "1", stringBuilder2);
}else{//叶子结点
//到叶子结点说明已经遍历到一个字符了,将该字符的哈夫曼编码加入huffmanCodes中
huffmanCodes.put(node.getContent(), stringBuilder2.toString());
}
}
}
//重载getCodes方法方便调用,只传入根结点
public static Map<Byte, String> getCodes(Node root){
//判断根节点是否为空
if(root == null){
return null;
}
//左递归
getCodes(root.getLeft(), "0", stringBuilder);
//右递归
getCodes(root.getRight(), "1", stringBuilder);
return huffmanCodes;
}
}
class Node implements Comparable<Node>{
private Byte content;//代表字符
private int count;//表示字符的权值(就是出现的次数)
private Node left;//左指针域
private Node right;//右指针域
public Node(Byte content, int count){
this.content = content;
this.count = count;
}
public Byte getContent() {
return content;
}
public void setContent(Byte content) {
this.content = content;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public Node getLeft() {
return left;
}
public void setLeft(Node left) {
this.left = left;
}
public Node getRight() {
return right;
}
public void setRight(Node right) {
this.right = right;
}
@Override
public String toString() {
return "Node{" +
"content=" + content +
", count=" + count +
'}';
}
@Override
public int compareTo(Node o) {
//从小到大排序
return this.getCount() - o.getCount();
}
//前序遍历
public void preOrder(){
System.out.println(this);
if(this.getLeft() != null){
this.getLeft().preOrder();
}
if(this.getRight() != null){
this.getRight().preOrder();
}
}
}
哈夫曼编码压缩
package com.weeks.tree.huffmancode;
import java.util.*;
/**
* @author 达少
* @version 1.0
*
* 哈夫曼编码:
* 以编码字符串"i like like like java do you like a java"为例
* 1.找到每个字符出现的次数:d-1,y-1,u-1,v-2,o-2,i-4,l-4,k-4,i-5,a-5,空格-9
* 2.将字符出现的次数作为权值构建哈夫曼树
* 3.构建好哈夫曼树后,按照左子树为0,右子树为1,从根节点开始找到代表每个字符的路径就是
* 哈夫曼编码
*
*/
public class HuffmanCode {
public static void main(String[] args) {
String content = "i like like like java do you like a java";
//将字符串转化字节数组
byte[] bytes = content.getBytes();
//封装压缩过程
byte[] huffmanZipBytes = huffmanZip(bytes);
System.out.println("压缩后的字节数组:" + Arrays.toString(huffmanZipBytes));
//分步完成哈夫曼压缩
/*
List<Node> nodes = getNodes(bytes);
Node root = createHuffmanTree(nodes);
root.preOrder();
getCodes(root);
System.out.println(huffmanCodes);
byte[] zipBytes = zip(bytes, huffmanCodes);
System.out.println(Arrays.toString(zipBytes));
*/
}
//获得构建哈夫曼树的结点
public static List<Node> getNodes(byte[] bytes){
//创建存储节点的List
List<Node> nodes = new ArrayList<>();
HashMap<Byte, Integer> map = new HashMap<>();
//统计每个字符出现的次数
for(byte b : bytes){
Integer count = map.get(b);
if(count == null){
map.put(b, 1);
}else{
map.put(b, count + 1);
}
}
//构建二叉树的结点
for (Map.Entry<Byte, Integer> entry: map.entrySet()){
nodes.add(new Node(entry.getKey(), entry.getValue()));
}
return nodes;
}
//构建哈夫曼树
public static Node createHuffmanTree(List<Node> nodes){
while(nodes.size() > 1){<以上是关于数据结构与算法(Java)之哈夫曼树及其应用的主要内容,如果未能解决你的问题,请参考以下文章