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