Mise en œuvre d'une structure chaînée
Nous voulons écrire une autre réalisation du TAD set en employant une structure de données constituée de cellules chaînées.
Nous poursuivons le principe utilisé dans la feuille précédente, en introduisant un niveau d’abstraction dans la réalisation de la structure chaînée:
- le type
linkprend en charge la gestion du chaînage entre les cellules de la structure chaînée. - le parcours de la structure chaînée et la gestion mémoire par allocation dynamique sont prises en charge par les fonctions du type
set
Cette partie se focalise sur la mise en œuvre de la structure chaînée link, muni des opérations suivantes:
type lelement;
llm__is_end_mark : lelement -> boolean
llm__next : lelement -> lelement
type link;
lnk__empty : link
lnk__add_head : link * lelement -> link
lnk__remove_head : link -> link * lelement
lnk__add_after : link * lelement * lelement -> link
lnk__remove_after : link * lelement -> link * lelement
lnk__first : link -> lelement
Nous ne donnons pas les axiomes cette fois-ci: il est aisé de déduire le comportement attendu de ces fonctions depuis leurs noms.
Pour la mise en œuvre, la structure chainée est constituée de cellules simplement chaînées contenant une valeur de type int. La cellule est représentée par le type struct lelement contenant les deux champs traditionnellement nommés data (de type int) et next (qui est un pointeur sur la cellule suivante struct lelement). Le champ next des cellules retirées de la structure chainée a une valeur NULL.
NB: les tests structurels du type link utilisent uniquement l’allocation automatique (même pour les cellules).
Représentation du type link
Une représentation fréquente d’une structure chaînée est d’utiliser le pointeur sur la première cellule nommé head (cf. ‘‘La programmation en pratique’’ KERNIGHAN, PIKE 1999-2001 ou “L’essentiel des structures de données en C’’ HOROWITZ, SAHNI, ANDERSON-FREED, 1993).

L’autre représentation habituelle sépare la structure chainée en deux structures C distinctes (cf. ‘‘Mastering Algorithms with C’’ LOUDON, 1999). L’expérence des mises en œuvre précédentes, nous a montré qu’il était préférable d’effectuer cette séparation. Nous définissons un type struct link avec un champ head contenant un pointeur sur la première cellule de la structure.

Il existe plusieurs manières d’encoder la fin de chaîne. Dans la suite, nous choisissons comme marque de fin une cellule sentinelle: une cellule de type struct lelement dont le champ next pointe sur elle-même.
L’allocation des structures struct link et struct lelement est prise en charge par les fonctions de l’ensemble (ou des tests). Les paramètres des fonctions sont donc considérés comme déjà alloués.
- le fichier
link/link.hcontient les définitions des typesstruct link(qui représente la liste chaînée) etstruct lelement(qui représente une cellule) ainsi que les prototypes des fonctions sur ces types. Il introduit également la constanteLLM_SENTINEL_PTRqui définit la cellule sentinelle. - le fichier
link/link.ccontient la définition de la sentinelle - enfin, le fichier
link/test_link_struc.ccontient un squelette de test structurel pour le typestruct link
Ajouter les trois fichiers ci-dessus dans votre dépôt (au bon endroit). Il est nécessaire de mettre à jour votre Makefile comme indiqué en annexe.
Initialisation/Cellule de tête/Marque de fin
Soit les fonctions:
lnk__emptyinitialise une structure chaînée à vide.lnk__firstretoune la cellule de tête, ou la marque de fin de chaînage pour une structure chaînée vide.llm__is_end_markindique si le paramètre d’entrée de typestruct lelementcorrespond à la marque de fin de chaînage (ici la cellule sentinelle).
- Comment la structure chaînée vide est-elle représentée?
- Programmer ces fonctions dans le fichier
link/link.c. - Écrire des tests structurels pour ces fonctions dans le fichier
link/test_link_struc.c.
Ajouter/Retirer en tête/Cellule suivante
On considère les trois fonctions:
lnk__add_headajoute une cellule au début de chaînage.lnk__remove_headretire la première cellule du chaînage. Elle retourne la cellule retirée ou une erreur si l’opération n’est pas possible.llm__nextretourne la cellule suivante de la cellule en paramètre d’entrée ou la marque de fin de chaînage.
-
Sur un schéma, montrer les traitements à effectuer pour les fonctions
lnk__add_headetlnk__remove_head. Par exemple, pour la fonctionlnk__remove_head, sur une liste non vide:
- Donner la complexité de chaque opération.
- Programmer ces fonctions dans le fichier
link/link.c. - Ajouter les tests structurels dans le fichier
link/test_link_struc.cpermettant de vérifier vos fonctions, notamment un parcours de la structure en vérifiant que les valeurs se trouvent dans l’ordre attendu.
Ajouter/Retirer après une cellule
Soit les deux fonctions:
lnk__add_afterqui prend au moins deux paramètres, la cellule à ajouter et la cellule après laquelle se fait l’ajout. Elle retourne une erreur si l’opération n’est pas possible.lnk__remove_afterretire la cellule située après la cellule passée en paramètre. Elle retourne la cellule retirée ou une erreur si l’opération n’est pas possible.
- Sur un schéma, montrer les traitements à effectuer pour chaque fonction.
- Donner la complexité de chacune.
- Programmer ces fonctions dans le fichier
link/link.c; - Ajouter les tests structurels dans le fichier
link/test_link_struc.cpermettant de vérifier vos fonctions.
Ajouter/Retirer en queue
Les deux fonctions suivantes ne sont pas à programmer
- La fonction
lnk__add_tailajoute une cellule en fin de chaînage.- Sur un schéma, montrer le traitement à effectuer.
- Donner la complexité.
- Proposer une solution pour améliorer la complexité.
- La fonction
lnk__remove_tailenlève la dernière cellule du chaînage.- Sur un schéma, montrer le traitement à effectuer.
- Que conclure pour la réalisation de cette opération pour cette mise en œuvre de
set? - À quelle condition cette opération est-elle réalisable sur
struct link?
Annexe
Il est nécessaire d’ajouter les directives ci-dessous à votre Makefile pour compiler les fonction développées dans cette partie.
Liste des modifications à apporter:
- Définir la variable
LNK_DIR=link - Ajouter la cible
link_strucà la variableBIN - Ajouter la directive de compilation
-I ${LNK_DIR}à la variableCPPFLAGS - Ajouter les règles suivantes pour
link(attention aux tabulations!)
#link
link.o: ${LNK_DIR}/link.c ${LNK_DIR}/link.h
${CC} ${CPPFLAGS} ${CFLAGS} ${LNK_DIR}/link.c -c
test_link_struc.o : ${LNK_DIR}/test_link_struc.c ${LNK_DIR}/link.h
${CC} ${CPPFLAGS} ${CFLAGS} ${LNK_DIR}/test_link_struc.c -c
link_struc: test_link_struc.o link.o
${CC} test_link_struc.o link.o -o $@ ${LDFLAGS}