Implémentation Java-arbre binaire de la structure de données (6): compression de données-codage Huffman

Structure de données de l'arbre binaire (6): compression de données-codage Huffman

Introduction de base: l'
arbre de Huffman est une sorte de codage à longueur de mot variable (VLC), qui est largement utilisé dans la compression de fichiers de données , et son taux de compression est généralement compris entre 20% et 90%.

  • 1. Méthode de traitement de l'information dans le domaine de la communication 1- codage à longueur fixe Le
    Insérez la description de l'image ici
    paragraphe ci-dessus en anglais contient 40 caractères, espaces compris, et leurs codes Ascii correspondants sont les suivants:
    Insérez la description de l'image ici
    Ensuite, ces codes Ascii sont convertis en binaire, et la
    Insérez la description de l'image ici
    longueur totale est de 359 , C'est-à-dire que l'utilisation d'un codage de longueur fixe pour envoyer cette phrase nécessite une longueur de 359.

  • 2. Méthode de traitement de l'information dans le domaine de la communication 2- codage à longueur variable
    Insérez la description de l'image ici
    Toujours cette phrase, on compte le nombre de caractères de cette phrase:
    d: 1, y: 1, u: 1, v: 2, o: 2, l: 4, e: 4, i: 5, a: 5, (space): 9
    Nous encodons en fonction du nombre d'occurrences de chaque caractère, plus il y a d'occurrences, plus l'encodage est petit, comme l'occurrence d'espaces Après 9 fois, le codage est 0. On
    obtient ce qui suit:
    0 = '(espace)', 1 = a, 10 = i, 11 = 3 100 = k, 101 = l, 110 = o, 111 = v, 1000 = j, 1001 = u, 1010 = y, 1011 = d

Selon le code stipulé par les règles ci-dessus, lorsque nous retransmettons cette phrase, le code devient:
10010110100 ... (je vais l'omettre, tout le monde comprend de toute façon)
Il y a un problème avec ce code. Par exemple, 1 peut être soit a, soit Il peut être considéré comme 10 ou i. Cela crée un conflit et doit être résolu d'une certaine manière
(le codage de caractères ne peut pas être le préfixe d'autres codages de caractères. Le codage qui répond à cette exigence est appelé codage de préfixe, c'est-à-dire qu'il ne peut pas correspondre au codage répété. Ce problème est résolu dans le codage Huffman)

**

3. Analyse du principe de codage de Huffman:

**
Insérez la description de l'image ici
1) Toujours cette phrase, nous comptons le nombre de caractères dans cette phrase:
d: 1, y: 1, u: 1, v: 2, o: 2, l: 4, e: 4, i: 5, a: 5, (espace): 9

2) Ensuite, nous construisons un arbre de Huffman en fonction du nombre d'occurrences des caractères ci - dessus , et le nombre de fois est utilisé comme poids .
(La méthode de construction de l'arbre de Huffman a été expliquée au chapitre 5, et la représentation du code n'est plus ici)

L'arbre de Huffman construit est montré dans la figure :
Insérez la description de l'image ici
3) Selon l'arbre de Huffman construit dans la figure ci-dessus, spécifiez le code pour chaque caractère, le chemin vers la gauche est 0, le chemin vers la droite est 1 et le code est le suivant
: o: 1000 , u: 10010, d: 100110, y: 100111, i: 101, a: 110, k: 1110, e: 1111, j: 0000, v: 0001, l: 001, (espace): 01

4) Selon le code Huffman ci-dessus,
Insérez la description de l'image ici
le code correspondant de la chaîne est (compression sans perte):
Insérez la description de l'image ici
ce code satisfait le code du préfixe, c'est-à-dire que le code du caractère ne peut pas être le préfixe d'autres codes de caractère, et ne provoquera pas l'ambiguïté de la correspondance. Il résout le problème des conflits de préfixes de la deuxième manière. La longueur est de 133.

Remarque:
Cet arbre de Huffman peut ne pas être exactement le même selon la méthode de tri (par exemple, lorsque plusieurs nœuds de l'arbre ont le même poids), les codes de Huffman correspondants ne sont pas exactement les mêmes. Mais wpl (la longueur de chemin pondérée de l'arbre) est la même, et les deux sont les plus petits.

Exemple de code :
1) Créer un nœud Node (data (data: stocker la valeur du code Acsii correspondant à chaque caractère), weight (weight: le nombre de fois où chaque caractère apparaît), gauche, droite)
2) Get: "j'aime comme comme java aimez-vous un tableau d'octet correspondant [] java
3) Ecrivez une méthode pour placer les nœuds Node qui sont prêts à construire l'arbre de Huffman dans la liste, sous la forme (Node [data = 97, weitht = 5], Node (data = 32, poids = 9), ...)
4) Construire un arbre de Huffman à travers la liste

Les étapes après le résumé sont :
1. Convertissez la chaîne en un tableau d'octets via la méthode getBytes ()
2. Utilisez la méthode getNodes (bytes) pour construire le tableau d'octets dans les nœuds feuilles de l'arbre de Huffman
3. Via createHuffmanTree (nœuds) La méthode génère un arbre de Huffman à partir des nœuds feuilles.
4. Utilisez l'arborescence Huffman pour créer la table de codes Huffman correspondant à la chaîne via la méthode getCodes (HuffmanRoot).
5. Utilisez la table de codes Huffman pour convertir la chaîne en une forme binaire via la méthode zipHuff (bytes, huffmanCodes), puis convertissez le binaire Converti en tableau d'octets correspondant.

Code de référence :

package Tree06;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class HuffmanCode {
    
    
	public static void main(String[] args) {
    
    
		String str="i like like like java do you like a java";
		byte[] contentBytes=str.getBytes();
		System.out.println("未压缩前的长度:"+contentBytes.length);
		byte[] huffmanZip = huffmanZip(contentBytes);
		System.out.println("将字符串转换成功后得到的byte数组"+Arrays.toString(huffmanZip));
	}
	/***
	 * 生成哈夫曼编码的总方法
	 * 
	 */
	private static byte[] huffmanZip(byte[] bytes) {
    
    
		//利用传进来的bytes构建哈夫曼树的节点
		List<Node> nodes=getNodes(bytes);
		//利用构建的节点生成哈夫曼树,返回该树的根节点
		Node HuffmanRoot=createHuffmanTree(nodes);
		//利用getCodes方法获取每个字符对应的哈夫曼编码,存入到huffmanCodes编码表中
		getCodes(HuffmanRoot);
		//利用得到的huffman编码表将对应的字符串转换为字节码数组
		byte[] zipHuff = zipHuff(bytes,huffmanCodes);
		return zipHuff;
	}
	
	
	/**
	 * 将字符串对应的byte[]数组,通过生成的哈夫曼编码表,生成哈夫曼编码
	 * @param bytes 原始的字符串对应的byte[](contentBytes)
	 * @param huffmanCodes生成的哈夫曼编码表
	 * @return 返回字符串对应的哈夫曼编码
	 * 此方法的目的是将目的字符串转换成对应的哈夫曼编码后,每八位一个字节装入byte数组里去
	 */
	public static byte[] zipHuff(byte[] bytes,Map<Byte,String>huffmanCodes) {
    
    
		//1.利用huffmanCodes将bytes转换成对应的字符串
		StringBuilder stringBuilder=new StringBuilder();
		for(byte b:bytes) {
    
    
			stringBuilder.append(huffmanCodes.get(b));
		}
		//统计返回byte[] huffmanCodeBytes长度
		int len;
		if(stringBuilder.length()%8==0) {
    
    
			len=stringBuilder.length()/8;
		}else {
    
    
			len=stringBuilder.length()/8+1;
		}
		byte[] huffmanCodeBytes=new byte[len];
		int index=0;
		for(int i=0;i<stringBuilder.length();i+=8) {
    
    
			String strByte;
			if(i+8>stringBuilder.length()) {
    
    
				strByte=stringBuilder.substring(i);
			}else {
    
    
				strByte=stringBuilder.substring(i,i+8);
			}
			//将strByte转成一个byte,放入到huffmanCodeBytes
			huffmanCodeBytes[index]=(byte)Integer.parseInt(strByte, 2);
			index++;
		}
		return huffmanCodeBytes;
	}
	
	
	//生成哈夫曼编码:
	/**
	 * 1.将哈夫曼编码表存放在Map<Byte,String>形式:字符的ascii码->对应的哈夫曼编码
	 * 2.拼接路径,利用StringBuilder存储某个叶子节点的路径
	 * @param root
	 */
	static Map<Byte,String> huffmanCodes=new HashMap<Byte,String>();
	static StringBuilder stringBuilder=new StringBuilder();
	
	//重载getCodes
	private static Map<Byte,String> getCodes(Node root){
    
    
		if(root==null) {
    
    
			return null;
		}
		//处理root的左子树
		getCodes(root.left,"0",stringBuilder);
		//处理右子树
		getCodes(root.right,"1",stringBuilder);
		return huffmanCodes;
	}
	
	//生成传入的node节点的所有叶子节点的哈夫曼编码,并放入集合,code:路径
	private static void getCodes(Node node,String code,StringBuilder stringBuilder) {
    
    
		StringBuilder stringBuilder2=new StringBuilder(stringBuilder);
		stringBuilder2.append(code);
		if(node!=null) {
    
    
			if(node.data==null) {
    
    
				//递归处理
				getCodes(node.left,"0",stringBuilder2);
				getCodes(node.right,"1",stringBuilder2);
			}else {
    
    //说明是一个叶子节点
				//表示找到某个叶子节点
				huffmanCodes.put(node.data, stringBuilder2.toString());
			}
		}
	}
	//前序遍历
		public static void frontShow(Node root) {
    
    
			if(root!=null) {
    
    
				root.frontShow();
			}else {
    
    
				System.out.println("树为空");
			}
		}
	
	private static List<Node> getNodes(byte[] bytes){
    
    
		//创建List
		ArrayList<Node> nodes=new ArrayList<Node>();
		//得到:"i like like like java do you like a java"对应的byte[]数组
		Map<Byte,Integer> counts=new HashMap<>();
		for(byte b:bytes) {
    
    
			Integer count=counts.get(b);
			if(count==null) {
    
    
				counts.put(b,1);
			}else {
    
    
				counts.put(b, count+1);
			}
		}
		
		//把每一个键值对转成一个Node对象,并加入到nodes集合
		for(Map.Entry<Byte,Integer> entry:counts.entrySet()) {
    
    
			nodes.add(new Node(entry.getKey(),entry.getValue()));
		}
		return nodes;
	}
	//通过List创建对应的哈夫曼树
	private static Node createHuffmanTree(List<Node> nodes) {
    
    
		while(nodes.size()>1) {
    
    
			Collections.sort(nodes);
			//取出第一颗最小的二叉树
			Node leftNode=nodes.get(0);
			Node rightNode = nodes.get(1);
			//创建一棵新的二叉树(没有data,只有权值)
			Node parent=new Node(null,leftNode.weight+rightNode.weight);
			parent.left=leftNode;
			parent.right=rightNode;
			
			//将已经处理两颗二叉树移除
			nodes.remove(leftNode);
			nodes.remove(rightNode);
			//加入新的
			nodes.add(parent);
			
		}
		
		return nodes.get(0);
	}
	
	
}

//创建Node
class Node implements Comparable<Node>{
    
    
	Byte data;//存放数据:字符对应的ascii码值
	int weight;//权值,字符出现的次数
	Node left;
	Node right;
	public Node(Byte data, int weight) {
    
    
		super();
		this.data = data;
		this.weight = weight;
	}
	@Override
	public int compareTo(Node o) {
    
    
		// TODO Auto-generated method stub
		//升序
		return this.weight-o.weight;
	}
	@Override
	public String toString() {
    
    
		return "Node [data=" + data + ", weight=" + weight + "]";
	}
	
	//前序遍历
	public void frontShow() {
    
    
		System.out.println(this);
		if(this.left!=null) {
    
    
			this.left.frontShow();
		}
		if(this.right!=null) {
    
    
			this.right.frontShow();
		}
	}
}


Résultat de sortie:
Insérez la description de l'image ici

Je suppose que tu aimes

Origine blog.csdn.net/qq_45273552/article/details/109129499
conseillé
Classement