2025-12-18 00:19:25 +01:00
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 ;
2025-12-18 12:34:39 +01:00
2025-12-18 00:19:25 +01:00
/**
* Implémente un arbre de Huffman utilisé pour la compression de données.
* <p>
* 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.
* </p>
*
* <p>
* 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.
* </p>
*
* <p>
* 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.
* </p>
*
* <p>
* 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.
* </p>
*
2025-12-18 12:34:39 +01:00
* @author Algassimou Pellel Diallo,Ayoub Anhdire
2025-12-18 00:19:25 +01:00
* @version 1.0
* @since 2025-12-13
*/
public class HuffmanTree {
/**
* Racine de l'arbre de Huffman.
* <p>
* 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.
* </p>
*/
private HuffmanNode root ;
2025-12-18 12:34:39 +01:00
/**
* Dictionnaire pour enregistrer les codes Huffman
*/
2025-12-20 14:01:36 +01:00
private static Map < Integer , Integer > codes ;
2025-12-18 12:34:39 +01:00
/**
* 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 ;
2025-12-18 00:19:25 +01:00
/**
* Construit un arbre de Huffman.
* <p>
* 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.
* </p>
*
* <p>
* 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.
* </p>
*/
public HuffmanTree ( int [ ] freq ) {
// J'initialise la racine à null.
this . root = null ;
// je cree une collection de feuilles
/////////////////////////////////// Voir si ya moyen doptimiser //////////////////////////////////////
2025-12-18 12:34:39 +01:00
// tu pourrait utiliser une PriorityQueue pour placer selon les fréquences comme dit dans l'énoncé !!!!
2025-12-18 00:19:25 +01:00
List < HuffmanNode > 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 ) ;
}
}
2025-12-18 12:34:39 +01:00
// On tri les feuilles par frequence croissante j'utilise un comparator qui compare la valeur retournee par getFrequence de chaque feuille
2025-12-18 00:19:25 +01:00
// 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
2025-12-15 20:43:18 +01:00
2025-12-18 00:19:25 +01:00
// 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 ) ;
2025-12-15 20:43:18 +01:00
2025-12-18 00:19:25 +01:00
// je crée un nœud interne en les combinant
HuffmanNode parent = new HuffmanNode ( left , right ) ;
2025-12-15 20:43:18 +01:00
2025-12-18 00:19:25 +01:00
// 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
2025-12-18 12:34:39 +01:00
while ( index < feuilles . size ( ) & & feuilles . get ( index ) . getFrequence ( ) < parent . getFrequence ( ) ) {
2025-12-18 00:19:25 +01:00
index + + ;
}
feuilles . add ( index , parent ) ;
}
2025-12-15 20:43:18 +01:00
2025-12-18 00:19:25 +01:00
// a la fin il ne reste qu'un seul noeud : la racine de l'arbre
this . root = feuilles . get ( 0 ) ;
2025-12-15 20:43:18 +01:00
}
2025-12-15 20:55:24 +01:00
2025-12-18 00:19:25 +01:00
/**
* Retourne la racine de l'arbre de Huffman.
* <p>
* 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.
* </p>
*
* @return le nœud racine de l'arbre de Huffman
*/
public HuffmanNode getRoot ( ) {
return root ;
}
2025-12-18 12:34:39 +01:00
/**
* @return Map on stockera les codes Huffman sous forme de dictionnaire
*/
2025-12-20 14:01:36 +01:00
public Map < Integer , Integer > generateCodes ( ) {
2025-12-18 12:34:39 +01:00
/**
* 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
2025-12-15 20:55:24 +01:00
*/
2025-12-18 12:34:39 +01:00
this . codes = new HashMap < > ( ) ;
this . chaineCarac = new String ( ) ;
if ( root . isLeaf ( ) ) {
2025-12-20 14:01:36 +01:00
codes . put ( root . getValue ( ) , Integer . parseInt ( chaineCarac ) ) ;
2025-12-18 12:34:39 +01:00
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 ;
}
2025-12-15 20:55:24 +01:00
2025-12-20 14:01:36 +01:00
public static Map < Integer , Integer > getDictionnary ( ) {
return codes ;
2025-12-18 12:56:55 +01:00
}
2025-12-15 20:55:24 +01:00
2025-12-18 12:34:39 +01:00
}