Département Informatique Département Informatique
Structures de données – partie 4 Arbres non binaires
Arbre lexical
Tables de hachage
Programmation objet
Cours n°9
Département Informatique
Département Informatique Arbres non binaires
Nous avons vu l'importance des structures de type arbre binaire pour la recherche d'éléments par la valeur.
Cette structure n'est possible que si la recherche peut utiliser un critère de séparation binaire.
Le temps de parcours est très faible (logarithmique)
La structure n'est néanmons pas adaptée à tous les cas (critère de sélection non binaire, grand nombre de valeurs identiques à stocker, arbre très déséquilibré)
Dans certains cas l'utilisation d'un arbre n-aire (avec n>2) est préférable.
Département Informatique
Département Informatique Principe
A chaque feuille de l'arbre correspond au plus n feuilles « enfants ».
Il faut donc un critère permettant de choisir, le plus rapidement possible, la « branche » fille à explorer.
On peut voir les implémentations possibles de deux manières : Une feuille possède plusieurs enfants (par exemple une liste chaînée, un tableau, etc...)
Une feuille possède un enfant et une soeur Les performances sont similaires dans les deux cas.
Si le nombre de soeurs est trop important, le temps de parcours est trop long, sauf si on utilise une structure de type... arbre de recherche !
Département Informatique
Département Informatique Exemple d'arbre non binaire : un arbre lexical
Nous allons stocker des mots dans un arbre.
Les mots sont composés de lettres, donc 26 valeurs possibles si l'on ne distingue pas la casse et qu'on n'utilise pas les accents.
Afin d'avoir une recherche rapide sur un mot, un arbre de type lexical est très intéressant.
Dans ce type d'arbre, les feuilles contiennent des caractères Un rang dans l'arbre correspond à une position dans le mot.
Département Informatique
Département Informatique Exemple
L'arbre lexical suivant contient les mots :
Arbre, art,des, dessin.
A
R B
T*
R E*
D E S*
S I
N*
La recherche peut aller très vite :
Bois n'est pas dans l'arbre (1 itération)
Desert n'est pas dans l'arbre (4 itérations)
Dessin est dans l'arbre (6 itérations)
Le nombre maximal d'itérations est égal à la profondeur de l'arbre, donc au nombre de lettres Ex : en
français 25
On indique par un booléen que la feuille est « finale »
Département Informatique
Département Informatique Exemple d'implémentation en C++
Pour chaque feuille ou noeud de l'arbre, les informations nécessaires :
• La lettre à stocker (char)
• Indication de fin de mot (bool)
• Feuilles suivantes (filles, au plus 26)
Il y a plusieurs méthodes pour lier les feuilles suivantes :
• Tableau de 26 cases (NULL par défaut)
• Liste chaînée de feuilles soeurs
• Arbre de recherche suivant la valeur Facile à programmer Très rapide à chercher Espace mémoire moyen Aucune n'est préférable, le contexte
décidera de la plus adaptée.
Simple à programmer Rapide à chercher (indice) Espace mémoire important
Programmation délicate Long à chercher (parcours)
Espace mémoire restreint
Département Informatique
Département Informatique Trois implémentations possibles
struct feuille {
char lettre;
bool final;
pfeuille filles[26];
};
struct feuille {
char lettre;
bool final;
pfeuille fille_suivante;
pfeuille soeur_suivante;
};
struct feuille {
bool final;
std::map<char, pfeuille>
filles;
};
typedef feuille* pfeuille;
Département Informatique Département Informatique
On a bien évidemment tout intérêt à utiliser des pointeurs intelligents pour l'arbre afin de supprimer les risques de fuite mémoire (ou pire, de double libération) : néanmoins ceci ajoute de l'espace mémoire (1 pointeur intelligent = 2 « vrais »
pointeurs + 1 entier, soit 96 bits au lieu de 32)
Une feuille prend environ 64 bits pour une seule lettre.
Nous allons choisir pour détailler la solution du tableau de feuilles,
en utilisant un std::vector< iut::shared_ptr< feuille > >
Bien sûr, un feuille[26] ferait l'affaire, mais avec du code en plus pour
Département Informatique
Département Informatique Implémentation C++
class arbre_lexical {
typedef shared_ptr<feuille> pfeuille;
struct feuille {
char lettre;
bool final;
std::vector<pfeuille> filles;
feuille(char l, bool f = false):
lettre(l),final(f), filles(26){}
static int indice(char c){return c-'A');}
};
pfeuille racine;
public:
void ajoute(const std::string& mot);
bool cherche(const std::string& mot) const;
};
Département Informatique
Département Informatique Ajout d'un mot dans l'arbre
• Parcours du mot lettre à lettre (itération ou récursivité)
• Passage de chaque lettre en majuscule (perte de la casse)
• Filtrage des eventuels « mauvais » caractères (exceptions)
• Si la lettre n'est pas présente dans l'arbre au rang voulu, la rajouter
• Si la lettre est la dernière du mot, placer le booléen à vrai
• Si la lettre n'est pas la dernière, recommancer au rang suivant
Comme souvent dans les arbres, l'algorithme le plus simple sera récursif.
On remarque que la notion de rang est ici importante : le paramètre sera nécessaire à la fonction récursive.
Une fonction d'ajout pour la feuille sera donc utile.
De même, on transmettra le mot en entier à la feuille.
Département Informatique Département Informatique
void arbre_lexical::ajoute(const std::string& mot) { racine->ajoute(mot,0);
} Une racine doit être présente
On commence par le rang 0 (première lettre)
arbre_lexical::arbre_lexical()
:racine(new feuille(0,false)) {}
Racine systématique
arbre_lexical::feuille::feuille(char l, bool f = false)
:lettre(l),final(f), filles(26){} 26 lettres
Département Informatique Département Informatique
void arbre_lexical::feuille::ajoute(const std::string& mot, int rang)
{
char L = Lettre(mot,rang);
if(!EstCorrecte(L))
throw erreur_arbre("Mauvais caractere");
bool is_final = EstFinal(mot,rang);
int i = indice(L); // passage en indice if(filles[i]==NULL)
filles[i] = new feuille(L,is_final);
if(!is_final)
filles[i]->ajoute(mot,rang+1);
Passage en indice à partir du caractère
On continue au rang suivant
Département Informatique
Département Informatique Recherche dans l'arbre
• Parcourir lettre à lettre le mot à chercher
• Normaliser la lettre (perte de la casse, etc...)
• Si la lettre n'est pas présente au rang indiqué, c'est raté
• Si la lettre est la dernière du mot, voir si la feuille est "finale"
• Si tout ok, continuer au rang suivant (récursivité ou itération)
Nous allons procéder comme pour l'ajout...
Département Informatique Département Informatique
bool arbre_lexical::cherche (const std::string& mot) const {
return racine->cherche(mot,0);
}
La recherche ne modifie pas l'objet
On cherche à partir de la racine pour le rang 0
(première lettre)
Département Informatique Département Informatique
bool arbre_lexical::feuille::cherche
(const std::string& mot, int rang) const {
bool retour = false,
is_final = (rang>0)?EstFinal(mot, rang-1):false; int i = indice(Lettre(mot,rang));
if(is_final)
retour = final;
else if(filles[i]!=NULL) retour =
filles[i]->cherche(mot, rang+1);
return retour;
}
Décalage dû à la première feuille de
l'arbre
On continue au rang suivant
Département Informatique Département Informatique
Avec une telle structure de données, l'utilisation d'un dictionnaire complet est aisée : la recherche est très rapide. Cette structure permet même
assez facilement d'implémenter des fonctionnalités de complétion automatique du mot...
Test avec un dictionnaire complet de 75000 mots :
16 ms de temps CPU avec un std::vector<std::string>
<1ms de temps CPU avec un arbre lexical
30 MO occupés par l'arbre lexical (avec cette impl.) 4 MO occupés par le tableau
Département Informatique
Département Informatique Tables de hachage
On remarque que l'arbre précédent est rapide parce qu'on retrouve directement l'indice à partir de la valeur à stocker.
Une telle méthode s'appelle du hachage.
Une fonction de hachage est une fonction associant pour une valeur, un entier donné (toujours le même !) avec si possible une seule valeur entière pour une valeur.
Une table de hachage est une structure de données de type tableau où les éléments sont rangés (indéxés) en fonction de leur contenu, grâce à la fonction de hachage.
La structure peut être extrêmement rapide avec une bonne fonction de hachage.
Département Informatique Département Informatique
Prochain cours : L'héritage
principes
modes d'héritage syntaxe en C++
A la semaine prochaine !