Structures autoréférentielles - Arbre binaire
Supposons que nous voulions traiter le problème général consistant à compter le nombre d'occurrences de tous les mots lus en entrée et de les trier.
Puisque la liste de mots n'est pas connue à l'avance, il n'est pas pratique de la trier et d'utiliser une recherche dichotomique.
Cependant, nous ne pouvons pas non plus effectuer une recherche séquentielle à l'arrivée de chaque mot pour vérifier si on l'a déjà rencontré ; le temps d'exécution du programme serait trop long.
Comment pouvons-nous organiser les données pour faire face efficacement à liste de mots arbitraires ?
Nous allons utiliser une structure appelée arbre binaire. L'arbre contient un "nœud" pour chaque mot, chaque nœud contient :
- un pointeur sur le texte du mot
- un compte du nombre d'occurrences
- un pointeur sur le nœud fils gauche
- un pointeur sur le nœud fils droit
Aucun nœud ne peut avoir plus de deux fils ; il est possible qu'un nœud n'en ait qu'un seul, ou aucun.
On place les nœuds de telle façon que les sous-arbre gauche d'un nœud quelconque contienne uniquement des mots de valeurs lexicographique inférieur au mot de ce nœud, et le sous-arbre droit, des mots de valeurs lexicographique supérieure.
Code source du fichier : btree.c
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#define SIZECACHE 100
#define MAXWORD 100
char cache[SIZECACHE]; /* tampon pour writeChar */
int pcache = 0; /* prochaine position
libre dans cache */
struct node {
char *word;
int cpt;
struct node *left;
struct node *right;
};
/* readWord : lit le mot ou le caractere suivant
sur l'entree */
int readWord (char *word, int lim)
{
int c, readChar(void);
void writeChar(int);
char *m = word;
while (isspace(c = readChar()))
;
if (c != EOF)
*m++ = c;
if (!isalpha(c)) {
*m = '\0';
return c;
}
for (; --lim > 0; m++)
if (!isalnum(*m = readChar())) {
writeChar(*m);
break;
}
*m = '\0';
return word[0];
}
/* lit un caractere (eventuellement remis sur l'entree) */
int readChar(void)
{
return (pcache > 0) ? cache[--pcache] : getchar();
}
/* remet c sur l'entree */
void writeChar(int c)
{
if (pcache >= SIZECACHE)
printf("writeChar : trop de caractere\n");
else
cache[pcache++] = c;
}
/* allocNode cree un noeud */
struct node *allocNode(void) {
return (struct node *) malloc (sizeof(struct node));
}
/* copyString : copie la chaine de caractere donnee en argument
dans un endroit sur obtenu par un appel a malloc */
char *copyString(char *s)
{
char *p;
p = (char *) malloc(strlen(s)+1); /* +1 pour '\0' */
if (p != NULL)
strcpy (p, s);
return p;
}
/* addTree : ajoute un noeud contenant la chaine m au niveau de p ou en dessous */
struct node *addTree(struct node *p, char *m) {
int cond;
if (p == NULL) {
p = allocNode();
p->word = copyString(m);
p->cpt = 1;
p->left = p->right = NULL;
} else if ((cond = strcmp(m, p->word)) == 0) {
p->cpt++;
} else if (cond < 0) {
/* mot dans le sous arbre gauche */
p->left = addTree(p->left, m);
} else
/* mot dans le sous arbre droit */
p->right = addTree(p->right, m);
return p;
}
/* Affiche l'arbre passe en parametre */
void displayTree(struct node *p)
{
if (p != NULL) {
displayTree(p->left);
printf("%4d %s\n", p->cpt, p->word);
displayTree(p->right);
}
}
int main()
{
struct node *root;
char word[MAXWORD];
root = NULL;
while (readWord(word, MAXWORD) != EOF)
if (isalpha(word[0]))
root = addTree(root, word);
displayTree(root);
return 0;
}
Exécution
$gcc btree.c -o btree $chmod +x btree $./btree
Le programme attends la saisie utilisateur à savoir une liste de mots. La saisie se termine en saississant EOF (ctrl+d)
un pointeur sur le texte du mot un compte du nombre d'occurrences un pointeur sur le noeud fils gauche un pointeur sur le noeud fils droit EOF
Affichage du résultat
1 compte 1 d 1 droit 2 du 2 fils 1 gauche 3 le 1 mot 2 noeud 1 nombre 1 occurrences 3 pointeur 3 sur 1 texte 4 un
btree.c