Modifications apportés

This commit is contained in:
ANHDIRE
2026-01-03 13:08:16 +01:00
parent bceb70c052
commit 5cd5f4c044
34 changed files with 2566 additions and 2519 deletions
@@ -1,72 +1,61 @@
package fr.iutfbleau.sae.mhuffman;
import java.util.*;
public class CanonicalCode{
//private Map<Integer, String> codeLengths = new HashMap<>();
//private Map<Integer, String> canonicalCodes = new HashMap<>();
public Map<Integer,String> generateCodes(Map<Integer,String> codesHuffman){
// 1 ere chose à faire : on regarde uniquement la longueur des codes initiaux(Huffman)
// 2eme chose à faire : remettre dans l'ordre des longueurs : si meme taille ==> regarder valeur
// 3eme chose à faire : ecriture des codes canoniques
// on recupere les entrées des codes Huffman pour pouvoir les triés
List<Map.Entry<Integer, String>> liste = new ArrayList<>(codesHuffman.entrySet());
// ici on comparer par longueur de la valeur ou sinon par la clé
// =============== ATTENTION CLASSE ANONYME !!!!!!! ================
Collections.sort(liste, new Comparator<Map.Entry<Integer, String>>() {
@Override
public int compare(Map.Entry<Integer, String> arg1 ,Map.Entry<Integer, String> arg2) {
int length1 = arg1.getValue().length();
int length2 = arg2.getValue().length();
if (length1 != length2) {
return length1 - length2;
}
return arg1.getKey() - arg2.getKey();
}
});
Map<Integer,String> canonicalCodes = new HashMap<>();
int code = 0; // code canonique à attribuer
int temp = 0; //garde la longueur du code précedent , pour gérer le décalage
for (Map.Entry<Integer, String> entree : liste ) {
int valeur = entree.getKey(); // symbole actuel
int longueur = entree.getValue().length(); // longueur du code actuel
code <<= (longueur - temp); // permet de décaler le code actuel si la longueur augmente
String codeBinaire = Integer.toBinaryString(code);
// permet d'ajouter des zeros si nécessaire !!!
while (codeBinaire.length() < longueur) {
codeBinaire = "0" + codeBinaire;
}
canonicalCodes.put(valeur,codeBinaire); // ajout dans le dictionnaire
code++; // incrémentation pour la valeur qui suit
temp = longueur; // mise à jour de la longueur précedente
}
return canonicalCodes;
}
public String getCode(Map<Integer,String> canonicalCodes,int value){
return canonicalCodes.get(value);
}
public int getLength(Map<Integer,String> codesH,int value){
return codesH.get(value).length();
}
}
package fr.iutfbleau.sae.mhuffman;
import java.util.*;
public class CanonicalCode{
//private Map<Integer, String> codeLengths = new HashMap<>();
//private Map<Integer, String> canonicalCodes = new HashMap<>();
public Map<Integer,String> generateCodes(Map<Integer,String> codesHuffman){
// 1 ere chose à faire : on regarde uniquement la longueur des codes initiaux(Huffman)
// 2eme chose à faire : remettre dans l'ordre des longueurs : si meme taille ==> regarder valeur
// 3eme chose à faire : ecriture des codes canoniques
// on recupere les entrées des codes Huffman pour pouvoir les triés
List<Map.Entry<Integer, String>> liste = new ArrayList<>(codesHuffman.entrySet());
// ici on comparer par longueur de la valeur ou sinon par la clé
Collections.sort(liste, new ComparateurCanonique());
Map<Integer,String> canonicalCodes = new HashMap<>();
int code = 0; // code canonique à attribuer
int temp = 0; //garde la longueur du code précedent , pour gérer le décalage
for (Map.Entry<Integer, String> entree : liste ) {
int valeur = entree.getKey(); // symbole actuel
int longueur = entree.getValue().length(); // longueur du code actuel
code <<= (longueur - temp); // permet de décaler le code actuel si la longueur augmente
String codeBinaire = Integer.toBinaryString(code);
// permet d'ajouter des zeros si nécessaire !!!
while (codeBinaire.length() < longueur) {
codeBinaire = "0" + codeBinaire;
}
canonicalCodes.put(valeur,codeBinaire); // ajout dans le dictionnaire
code++; // incrémentation pour la valeur qui suit
temp = longueur; // mise à jour de la longueur précedente
}
return canonicalCodes;
}
public String getCode(Map<Integer,String> canonicalCodes,int value){
return canonicalCodes.get(value);
}
public int getLength(Map<Integer,String> codesH,int value){
return codesH.get(value).length();
}
}
@@ -0,0 +1,19 @@
package fr.iutfbleau.sae.mhuffman;
import java.util.Comparator;
import java.util.Map;
public class ComparateurCanonique implements Comparator<Map.Entry<Integer, String>> {
@Override
public int compare(Map.Entry<Integer, String> entree1,Map.Entry<Integer, String> entree2) {
int longueur1 = entree1.getValue().length();
int longueur2 = entree2.getValue().length();
if (longueur1 != longueur2) {
return longueur1 - longueur2;
}
return entree1.getKey() - entree2.getKey();
}
}
+115 -115
View File
@@ -1,115 +1,115 @@
package fr.iutfbleau.sae.mhuffman;
import fr.iutfbleau.sae.mimage.RGBImage;
/**
* Représente une table de fréquences pour une image RGB.
* <p>
* Cette classe est utilisée dans le cadre de la compression de Huffman.
* Elle compte le nombre d'occurrences de chaque valeur possible (0 à 255)
* pour chacune des composantes de couleur : rouge, vert et bleu.
* </p>
*
* <p>
* Chaque composante est stockée dans un tableau de taille 256 :
* <ul>
* <li>{@code freqR} pour le rouge</li>
* <li>{@code freqG} pour le vert</li>
* <li>{@code freqB} pour le bleu</li>
* </ul>
* L'indice du tableau correspond à la valeur de la composante,
* et la valeur stockée correspond à sa fréquence d'apparition.
* </p>
*
* <p>
* Cette table de fréquences sert ensuite à construire les arbres de Huffman
* nécessaires à la compression des données de l'image.
* </p>
*
* @author Algassimou Pellel Diallo
* @version 1.0
* @since 2025-12-13
*/
public class FrequencyTable {
/** Tableau des fréquences pour la composante rouge (valeurs de 0 à 255). */
private int[] freqR;
/** Tableau des fréquences pour la composante verte (valeurs de 0 à 255). */
private int[] freqG;
/** Tableau des fréquences pour la composante bleue (valeurs de 0 à 255). */
private int[] freqB;
/**
* Construit une table de fréquences vide.
* <p>
* Les trois tableaux de fréquences sont initialisés
* avec 256 cases mises à zéro.
* </p>
*/
public FrequencyTable() {
this.freqR = new int[256];
this.freqG = new int[256];
this.freqB = new int[256];
}
/**
* Calcule les fréquences des composantes RGB à partir d'une image.
* <p>
* Pour chaque pixel de l'image, la méthode récupère les valeurs
* rouge, verte et bleue, puis incrémente la case correspondante
* dans le tableau de fréquences associé.
* </p>
*
* @param img l'image RGB à analyser
*/
public void computeFromImage(RGBImage img) {
/*Nb: une composante de couleur est un entier entre 0 et 255 qui représente la part de rouge,vert ou bleu
dans la couleur d'un pixel.
*/
/* pour chaque composante de couleur de chaque pixel, on incrémente la fréquence correspondante,
c'est-à-dire on compte le nombre de fois que la composante apparaît dans l'image.
ex: si un pixel P a une composante rouge de 150, on incrémente freqR[150] de 1.
puis on fait de même pour les composantes verte et bleue.
on répète ce processus pour tous les pixels de l'image.
*/
for (int ligne = 0; ligne < img.getWidth(); ligne++) {
for (int colonne = 0; colonne < img.getHeight(); colonne++) {
this.freqR[img.getPixel(ligne, colonne).getR()]++; // Incrémente la fréquence de la composante rouge
this.freqG[img.getPixel(ligne, colonne).getG()]++; // Incrémente la fréquence de la composante verte
this.freqB[img.getPixel(ligne, colonne).getB()]++; // Incrémente la fréquence de la composante bleue
}
}
}
/**
* Retourne le tableau des fréquences de la composante rouge.
*
* @return un tableau de 256 entiers représentant les fréquences du rouge
*/
public int[] getRed() {
return this.freqR;
}
/**
* Retourne le tableau des fréquences de la composante verte.
*
* @return un tableau de 256 entiers représentant les fréquences du vert
*/
public int[] getGreen() {
return this.freqG;
}
/**
* Retourne le tableau des fréquences de la composante bleue.
*
* @return un tableau de 256 entiers représentant les fréquences du bleu
*/
public int[] getBlue() {
return this.freqB;
}
}
package fr.iutfbleau.sae.mhuffman;
import fr.iutfbleau.sae.mimage.RGBImage;
/**
* Représente une table de fréquences pour une image RGB.
* <p>
* Cette classe est utilisée dans le cadre de la compression de Huffman.
* Elle compte le nombre d'occurrences de chaque valeur possible (0 à 255)
* pour chacune des composantes de couleur : rouge, vert et bleu.
* </p>
*
* <p>
* Chaque composante est stockée dans un tableau de taille 256 :
* <ul>
* <li>{@code freqR} pour le rouge</li>
* <li>{@code freqG} pour le vert</li>
* <li>{@code freqB} pour le bleu</li>
* </ul>
* L'indice du tableau correspond à la valeur de la composante,
* et la valeur stockée correspond à sa fréquence d'apparition.
* </p>
*
* <p>
* Cette table de fréquences sert ensuite à construire les arbres de Huffman
* nécessaires à la compression des données de l'image.
* </p>
*
* @author Algassimou Pellel Diallo
* @version 1.0
* @since 2025-12-13
*/
public class FrequencyTable {
/** Tableau des fréquences pour la composante rouge (valeurs de 0 à 255). */
private int[] freqR;
/** Tableau des fréquences pour la composante verte (valeurs de 0 à 255). */
private int[] freqG;
/** Tableau des fréquences pour la composante bleue (valeurs de 0 à 255). */
private int[] freqB;
/**
* Construit une table de fréquences vide.
* <p>
* Les trois tableaux de fréquences sont initialisés
* avec 256 cases mises à zéro.
* </p>
*/
public FrequencyTable() {
this.freqR = new int[256];
this.freqG = new int[256];
this.freqB = new int[256];
}
/**
* Calcule les fréquences des composantes RGB à partir d'une image.
* <p>
* Pour chaque pixel de l'image, la méthode récupère les valeurs
* rouge, verte et bleue, puis incrémente la case correspondante
* dans le tableau de fréquences associé.
* </p>
*
* @param img l'image RGB à analyser
*/
public void computeFromImage(RGBImage img) {
/*Nb: une composante de couleur est un entier entre 0 et 255 qui représente la part de rouge,vert ou bleu
dans la couleur d'un pixel.
*/
/* pour chaque composante de couleur de chaque pixel, on incrémente la fréquence correspondante,
c'est-à-dire on compte le nombre de fois que la composante apparaît dans l'image.
ex: si un pixel P a une composante rouge de 150, on incrémente freqR[150] de 1.
puis on fait de même pour les composantes verte et bleue.
on répète ce processus pour tous les pixels de l'image.
*/
for (int ligne = 0; ligne < img.getWidth(); ligne++) {
for (int colonne = 0; colonne < img.getHeight(); colonne++) {
this.freqR[img.getPixel(ligne, colonne).getR()]++; // Incrémente la fréquence de la composante rouge
this.freqG[img.getPixel(ligne, colonne).getG()]++; // Incrémente la fréquence de la composante verte
this.freqB[img.getPixel(ligne, colonne).getB()]++; // Incrémente la fréquence de la composante bleue
}
}
}
/**
* Retourne le tableau des fréquences de la composante rouge.
*
* @return un tableau de 256 entiers représentant les fréquences du rouge
*/
public int[] getRed() {
return this.freqR;
}
/**
* Retourne le tableau des fréquences de la composante verte.
*
* @return un tableau de 256 entiers représentant les fréquences du vert
*/
public int[] getGreen() {
return this.freqG;
}
/**
* Retourne le tableau des fréquences de la composante bleue.
*
* @return un tableau de 256 entiers représentant les fréquences du bleu
*/
public int[] getBlue() {
return this.freqB;
}
}
+119 -119
View File
@@ -1,119 +1,119 @@
package fr.iutfbleau.sae.mhuffman;
/**
* Représente un nœud de l'arbre de Huffman.
* <p>
* Un {@code HuffmanNode} peut être :
* <ul>
* <li>une feuille, contenant une valeur (symbole) et une fréquence</li>
* <li>un nœud interne, contenant uniquement une fréquence et deux enfants</li>
* </ul>
* </p>
*
* <p>
* Cette classe est une structure de données utilisée par {@code HuffmanTree}
* pour construire l'arbre de Huffman.
* </p>
*
* @author Algassimou Pellel Diallo
* @version 1.1
* @since 2025-12-17
*/
public class HuffmanNode {
/** Valeur de la composante (cette valeur est appelée symbole, voir l'histoire de huffman, tres interressant) représentée par ce nœud (si feuille). c'est la part de la composante (rouge, verte ou bleue) dans la couleur d'un pixel. */
private int value;
/** Fréquence du symbole (somme des fréquences des enfants). */
private int frequence;
/** Fils gauche du nœud (null si feuille). */
private HuffmanNode left;
/** Fils droit du nœud (null si feuille). */
private HuffmanNode right;
/**
* Construit un nœud feuille de Huffman.
* <p>
* Ce constructeur est utilisé pour représenter une valeur
* issue de la table de fréquences.
* </p>
*
* @param value la valeur (symbole) représentée par ce nœud
* @param frequence la fréquence d'apparition de la valeur
*/
public HuffmanNode(int value, int frequence) {
this.value = value;
this.frequence = frequence;
this.left = null;
this.right = null;
}
/**
* Construit un nœud interne de Huffman.
* <p>
* Ce constructeur est utilisé lors de la fusion de deux nœuds
* de plus faible fréquence lors de la construction de l'arbre.
* </p>
*
* @param left le fils gauche
* @param right le fils droit
*/
public HuffmanNode(HuffmanNode left, HuffmanNode right) {
this.left = left;
this.right = right;
this.frequence = left.frequence + right.frequence;
}
/**
* Indique si ce nœud est une feuille.
*
* @return {@code true} si le nœud est une feuille, {@code false} sinon
*/
public boolean isLeaf() {
return this.left == null && this.right == null;
}
/**
* Retourne la fréquence du nœud.
*
* @return la fréquence
*/
public int getFrequence() {
return this.frequence;
}
/**
* Retourne le fils gauche du nœud.
*
* @return le fils gauche
*/
public HuffmanNode getLeft() {
return this.left;
}
/**
* Retourne le fils droit du nœud.
*
* @return le fils droit
*/
public HuffmanNode getRight() {
return this.right;
}
/**
* Retourne la valeur représentée par ce nœud.
* <p>
* Cette méthode n'a de sens que si le nœud est une feuille.
* </p>
*
* @return la valeur du symbole
*/
public int getValue() {
if (!this.isLeaf()) {
throw new IllegalStateException("La valeur n'est définie que pour les feuilles.");
}
return this.value;
}
}
package fr.iutfbleau.sae.mhuffman;
/**
* Représente un nœud de l'arbre de Huffman.
* <p>
* Un {@code HuffmanNode} peut être :
* <ul>
* <li>une feuille, contenant une valeur (symbole) et une fréquence</li>
* <li>un nœud interne, contenant uniquement une fréquence et deux enfants</li>
* </ul>
* </p>
*
* <p>
* Cette classe est une structure de données utilisée par {@code HuffmanTree}
* pour construire l'arbre de Huffman.
* </p>
*
* @author Algassimou Pellel Diallo
* @version 1.1
* @since 2025-12-17
*/
public class HuffmanNode {
/** Valeur de la composante (cette valeur est appelée symbole, voir l'histoire de huffman, tres interressant) représentée par ce nœud (si feuille). c'est la part de la composante (rouge, verte ou bleue) dans la couleur d'un pixel. */
private int value;
/** Fréquence du symbole (somme des fréquences des enfants). */
private int frequence;
/** Fils gauche du nœud (null si feuille). */
private HuffmanNode left;
/** Fils droit du nœud (null si feuille). */
private HuffmanNode right;
/**
* Construit un nœud feuille de Huffman.
* <p>
* Ce constructeur est utilisé pour représenter une valeur
* issue de la table de fréquences.
* </p>
*
* @param value la valeur (symbole) représentée par ce nœud
* @param frequence la fréquence d'apparition de la valeur
*/
public HuffmanNode(int value, int frequence) {
this.value = value;
this.frequence = frequence;
this.left = null;
this.right = null;
}
/**
* Construit un nœud interne de Huffman.
* <p>
* Ce constructeur est utilisé lors de la fusion de deux nœuds
* de plus faible fréquence lors de la construction de l'arbre.
* </p>
*
* @param left le fils gauche
* @param right le fils droit
*/
public HuffmanNode(HuffmanNode left, HuffmanNode right) {
this.left = left;
this.right = right;
this.frequence = left.frequence + right.frequence;
}
/**
* Indique si ce nœud est une feuille.
*
* @return {@code true} si le nœud est une feuille, {@code false} sinon
*/
public boolean isLeaf() {
return this.left == null && this.right == null;
}
/**
* Retourne la fréquence du nœud.
*
* @return la fréquence
*/
public int getFrequence() {
return this.frequence;
}
/**
* Retourne le fils gauche du nœud.
*
* @return le fils gauche
*/
public HuffmanNode getLeft() {
return this.left;
}
/**
* Retourne le fils droit du nœud.
*
* @return le fils droit
*/
public HuffmanNode getRight() {
return this.right;
}
/**
* Retourne la valeur représentée par ce nœud.
* <p>
* Cette méthode n'a de sens que si le nœud est une feuille.
* </p>
*
* @return la valeur du symbole
*/
public int getValue() {
if (!this.isLeaf()) {
throw new IllegalStateException("La valeur n'est définie que pour les feuilles.");
}
return this.value;
}
}
+218 -218
View File
@@ -1,218 +1,218 @@
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;
// test
/**
* 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>
*
* @author Algassimou Pellel Diallo,Ayoub Anhdire
* @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;
/**
* Dictionnaire pour enregistrer les codes Huffman
*/
// j'ai retirer le static car chaque arbre a ses propres codes et j'utilise string plutot que int pour stocker les codes car on construit une chaine de 0 et de 1
private Map<Integer, String> 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.
* <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
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);
}
}
// 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);
}
// Méthode pour générer les codes Huffman à partir de l'arbre
// pourquoi string et pas int ? car on va construire une chaine de 0 et de 1
/**
* @return Map on stockera les codes Huffman sous forme de dictionnaire
*/
public Map<Integer,String> 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<>();
// je lance la methode recursive avec une chaine vide qui va se remplir au fur et à mesure
generateCodesRec(this.root, "");
return codes;
}
private void generateCodesRec(HuffmanNode node, String prefiixe) {
// Cas de base: si le noeud est une feuille, on ajoute le code au dictionnaire
if (node.isLeaf()) {
if (prefiixe.length() > 0){
this.codes.put(node.getValue(), prefiixe);
}else{
this.codes.put(node.getValue(), "0");
}
return;
}
//Case general : sinon on continue a parcourir l'arbre
// On va a gauche en ajoutant "0" au code
generateCodesRec(node.getLeft(), prefiixe + "0");
// On va a droite en ajoutant "1" au code
generateCodesRec(node.getRight(), prefiixe + "1");
// this.codes = new HashMap<>();
// this.chaineCarac = new String();
// if(root.isLeaf()){
// codes.put(root.getValue(),Integer.parseInt(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;
}
/**
* @return Dictionnaire des codes Huffman
*/
public Map<Integer,String> getCodes(){
return codes;
}
/**
* @return le nœud racine de l'arbre de Huffman
*/
public HuffmanNode getRoot() {
return root;
}
}
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;
// test
/**
* 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>
*
* @author Algassimou Pellel Diallo,Ayoub Anhdire
* @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;
/**
* Dictionnaire pour enregistrer les codes Huffman
*/
// j'ai retirer le static car chaque arbre a ses propres codes et j'utilise string plutot que int pour stocker les codes car on construit une chaine de 0 et de 1
private Map<Integer, String> 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.
* <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
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);
}
}
// 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);
}
// Méthode pour générer les codes Huffman à partir de l'arbre
// pourquoi string et pas int ? car on va construire une chaine de 0 et de 1
/**
* @return Map on stockera les codes Huffman sous forme de dictionnaire
*/
public Map<Integer,String> 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<>();
// je lance la methode recursive avec une chaine vide qui va se remplir au fur et à mesure
generateCodesRec(this.root, "");
return codes;
}
private void generateCodesRec(HuffmanNode node, String prefiixe) {
// Cas de base: si le noeud est une feuille, on ajoute le code au dictionnaire
if (node.isLeaf()) {
if (prefiixe.length() > 0){
this.codes.put(node.getValue(), prefiixe);
}else{
this.codes.put(node.getValue(), "0");
}
return;
}
//Case general : sinon on continue a parcourir l'arbre
// On va a gauche en ajoutant "0" au code
generateCodesRec(node.getLeft(), prefiixe + "0");
// On va a droite en ajoutant "1" au code
generateCodesRec(node.getRight(), prefiixe + "1");
// this.codes = new HashMap<>();
// this.chaineCarac = new String();
// if(root.isLeaf()){
// codes.put(root.getValue(),Integer.parseInt(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;
}
/**
* @return Dictionnaire des codes Huffman
*/
public Map<Integer,String> getCodes(){
return codes;
}
/**
* @return le nœud racine de l'arbre de Huffman
*/
public HuffmanNode getRoot() {
return root;
}
}