package fr.iutfbleau.sae.mhuffman; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Implémente un arbre de Huffman utilisé pour la compression de données. *

* La classe {@code HuffmanTree} est chargée de représenter la structure * de l'arbre de Huffman et de générer les codes binaires associés aux symboles. * Elle s'appuie sur la classe {@link HuffmanNode} pour représenter les nœuds * de l'arbre. *

* *

* L'arbre est construit à partir des fréquences des symboles calculées * en amont (par exemple à l'aide d'une {@code FrequencyTable}). * Chaque symbole est d'abord représenté par une feuille, puis les nœuds * sont combinés progressivement selon l'algorithme de Huffman afin * d'obtenir un arbre binaire optimal. *

* *

* Une fois l'arbre construit, celui-ci est parcouru afin de générer une * table de correspondance associant à chaque symbole un code binaire unique. * Les symboles les plus fréquents se retrouvent plus proches de la racine * et possèdent donc des codes plus courts, ce qui permet de réduire * la taille des données compressées. *

* *

* Cette classe ne s'occupe pas de la lecture ou de l'écriture des bits. * Elle fournit uniquement la structure et les informations nécessaires * à la compression, qui sont ensuite exploitées par des flux binaires * dédiés. *

* * @author Algassimou Pellel Diallo,Ayoub Anhdire * @version 1.0 * @since 2025-12-13 */ public class HuffmanTree { /** * Racine de l'arbre de Huffman. *

* Ce nœud est le résultat final de la construction de l'arbre et constitue * le point de départ pour la génération des codes binaires ainsi que * pour le décodage des données compressées. *

*/ private HuffmanNode root; /** * Dictionnaire pour enregistrer les codes Huffman */ private Map codes; /** * Chaine de caracteres qui va nous permettre de sauvegader le code Huffman * Permet en d'autres termes de construire la chaine de 1 et de 0 */ private String chaineCarac; /** * Construit un arbre de Huffman. *

* Le constructeur est responsable de l'initialisation de la structure * de l'arbre. En pratique, il combine les nœuds feuilles représentant * les symboles par ordre croissant de fréquence jusqu'à obtenir un * unique nœud racine. *

* *

* Les détails de la construction (structure de données utilisée, * ordre des fusions, etc.) sont volontairement séparés de la logique * de génération des codes. *

*/ public HuffmanTree(int[] freq) { // J'initialise la racine à null. this.root = null; // je cree une collection de feuilles /////////////////////////////////// Voir si ya moyen doptimiser ////////////////////////////////////// // tu pourrait utiliser une PriorityQueue pour placer selon les fréquences comme dit dans l'énoncé !!!! List feuilles = new ArrayList<>(); // pour chaque valeur(symbole) dans la table de frequence for (int i = 0; i < freq.length; i++) { // si la frequence est superieure a 0 , on cree une feuille if (freq[i] > 0) { // pour la valeur (symbole) i avec frequence freq[i], on cree une feuille HuffmanNode feuille = new HuffmanNode(i, freq[i]); // on ajoute la feuille à la collection feuilles.add(feuille); } } // On tri les feuilles par frequence croissante j'utilise un comparator qui compare la valeur retournee par getFrequence de chaque feuille // Referencement de methode avec :: feuilles.sort(Comparator.comparingInt(HuffmanNode::getFrequence)); // flemme de faire un algo de tri alors que java le fait tres bien a voir a la fin si je vais coder une liste chainee avec un tri par insertion personnalise // Fusion des nœuds jusqu'à obtenir la racine // Tant qu'il y a plus d'une feuille dans la collection while (feuilles.size() > 1) { // je prends les deux feuilles de plus faible fréquence HuffmanNode left = feuilles.remove(0); HuffmanNode right = feuilles.remove(0); // je crée un nœud interne en les combinant HuffmanNode parent = new HuffmanNode(left, right); // j'insère le nœud parent dans la collection à la bonne position pour maintenir l'ordre (plus performant qu'un tri complet à chaque itération) int index = 0; // tant que l'index est dans les limites et que la frequence du noeud à l'index est inférieure à celle du parent while (index < feuilles.size() && feuilles.get(index).getFrequence() < parent.getFrequence()) { index++; } feuilles.add(index, parent); } // a la fin il ne reste qu'un seul noeud : la racine de l'arbre this.root = feuilles.get(0); } /** * Retourne la racine de l'arbre de Huffman. *

* Cette méthode permet d'accéder à la structure complète de l'arbre, * notamment lors de la génération des codes ou du décodage des données. *

* * @return le nœud racine de l'arbre de Huffman */ public HuffmanNode getRoot() { return root; } /** * @return Map on stockera les codes Huffman sous forme de dictionnaire */ public Map generateCodes(){ /** * Le but de cette méthode est de pouvoir generer les codes Huffman à partir de l'arbre : * Les branches prendront comme valeur 1 ou 0 selon differents cas : * 1 - si on saute vers un fils droit * 0 - si on saute vers un fils gauche. * On construit les codes qui partent de la racine jusqu'à notre objectif */ this.codes = new HashMap<>(); this.chaineCarac = new String(); if(root.isLeaf()){ codes.put(root.getValue(),chaineCarac); return codes; } HuffmanNode temp = root; if (root.getLeft() != null) { root = root.getLeft(); chaineCarac = chaineCarac + "0"; generateCodes(); // on retire le dernier bit lorsqu'on remonte car sinon les codes seront faussés chaineCarac = chaineCarac.substring(0, chaineCarac.length() - 1); } if (temp.getRight() != null) { root = temp.getRight(); chaineCarac = chaineCarac + "1"; generateCodes(); chaineCarac = chaineCarac.substring(0, chaineCarac.length() - 1); } root = temp; return codes; } public static Map getDictionnary(){ return this.codes; } }