---
config:
look: 'handDrawn'
---
graph LR
A["[10 | @]"] --> B["[20 | @]"] --> C["[30 | NULL]"]
style A fill:#E6F3FF
style B fill:#E6F3FF
style C fill:#E6F3FF
XTI102 - Structures de Données Avancées
Exemple
Note
Le choix d’une structure de données dépend des opérations les plus fréquentes.
Tip
L’idée est d’utiliser une structure de données dynamique qui peut grandir et rétrécir selon les besoins et ce de manière efficace.
Comment stocker deux informations dans une même variable ?
Afin de stocker à la fois une donnée et un pointeur vers la cellule suivante, on utilise une structure en C.
---
config:
look: 'handDrawn'
---
graph LR
A["[10 | @]"] --> B["[20 | @]"] --> C["[30 | NULL]"]
style A fill:#E6F3FF
style B fill:#E6F3FF
style C fill:#E6F3FF
En quoi est-ce différent d’un tableau ?
Dans un tableau, les éléments sont contigus. Dans une liste, les cellules sont dispersées en mémoire.
Remarques
struct s_cell * next car le type t_cell n’est pas encore complètement défini.---
config:
look: 'handDrawn'
---
graph LR
subgraph t_list
head[head]
end
head[NULL]
style head fill:#FFE6E6
---
config:
look: 'handDrawn'
---
graph LR
subgraph t_list
head[head]
end
head --> A["[10 | @]"] --> B["[20 | @]"] --> C["[30 | NULL]"]
style t_list fill:#ecf98f
style head fill:#FFE6E6
style A fill:#E6F3FF
style B fill:#E6F3FF
style C fill:#E6F3FF
---
config:
look: 'handDrawn'
---
graph LR
subgraph t_list
head[head]
end
head --> A["[10 | @]"] --> B["[20 | @]"] --> C["[30 | NULL]"]
N["[5 | NULL]"]
style t_list fill:#ecf98f
style head fill:#FFE6E6
style N fill:#90EE90
style A fill:#E6F3FF
style B fill:#E6F3FF
style C fill:#E6F3FF
---
config:
look: 'handDrawn'
---
graph LR
subgraph t_list
head[head]
end
head --> A["[10 | @]"] --> B["[20 | @]"] --> C["[30 | NULL]"]
N["[5 | @]"] --> A
style t_list fill:#ecf98f
style head fill:#FFE6E6
style N fill:#90EE90
style A fill:#E6F3FF
style B fill:#E6F3FF
style C fill:#E6F3FF
---
config:
look: 'handDrawn'
---
graph LR
subgraph t_list
head[head]
end
head --> N["[5 | @]"] --> A["[10 | @]"] --> B["[20 | @]"] --> C["[30 | NULL]"]
style t_list fill:#ecf98f
style head fill:#FFE6E6
style N fill:#90EE90
style A fill:#E6F3FF
style B fill:#E6F3FF
style C fill:#E6F3FF
void insert_at_head(t_list * list, int value) {
// Créer une nouvelle cellule
t_cell * new_cell = create_cell(value);
// La nouvelle cellule pointe vers l'ancienne tête
new_cell->next = list->head;
// Mettre à jour la tête
list->head = new_cell;
}Pourquoi t_list * list ?
On passe un pointeur vers la structure t_list car elle contient le pointeur head que nous devons modifier.
void insert_at_head(t_list * list, int value) {
// Créer une nouvelle cellule
t_cell * new_cell = create_cell(value);
// La nouvelle cellule pointe vers l'ancienne tête
new_cell->next = list->head;
// Mettre à jour la tête
list->head = new_cell;
}Question
Est-ce que cette fonction reste valide si la liste est initialement vide ?
Comment parcourrir une liste chaînées si on n’a pas de système d’indice comme dans un tableau ?
cur à la tête de la listecur != NULL) :
cur = cur->next---
config:
look: 'handDrawn'
---
graph LR
subgraph t_list
head[head]
end
head --> A["[10 | @]"] --> B["[20 | @]"] --> C["[30 | NULL]"]
cur -.-> A
style t_list fill:#ecf98f
style head fill:#FFE6E6
style cur fill:#FFE6E6
style A fill:#E6F3FF
style B fill:#E6F3FF
style C fill:#E6F3FF
---
config:
look: 'handDrawn'
---
graph LR
subgraph t_list
head[head]
end
head --> A["[10 | @]"] --> B["[20 | @]"] --> C["[30 | NULL]"]
cur -.-> B
style t_list fill:#ecf98f
style head fill:#FFE6E6
style cur fill:#FFE6E6
style A fill:#E6F3FF
style B fill:#E6F3FF
style C fill:#E6F3FF
---
config:
look: 'handDrawn'
---
graph LR
subgraph t_list
head[head]
end
head --> A["[10 | @]"] --> B["[20 | @]"] --> C["[30 | NULL]"]
cur -.-> C
style t_list fill:#ecf98f
style head fill:#FFE6E6
style cur fill:#FFE6E6
style A fill:#E6F3FF
style B fill:#E6F3FF
style C fill:#E6F3FF
---
config:
look: 'handDrawn'
---
graph LR
subgraph t_list
head[head]
end
head --> A["[10 | @]"] --> B["[20 | @]"] --> C["[30 | NULL]"]
cur -.-> NULL
style t_list fill:#ecf98f
style head fill:#FFE6E6
style cur fill:#FFE6E6
style A fill:#E6F3FF
style B fill:#E6F3FF
style C fill:#E6F3FF
void print_list(t_list * list) {
t_cell * cur = list->head; // Initialiser le pointeur à la tête
printf("Liste : ");
while (cur != NULL) { // Tant que cur pointe sur une cellule
printf("%d -> ", cur->value); // Traiter la cellule courante
cur = cur->next; // Avancer le pointeur
}
printf("NULL\n");
}Cette fonction affichera :
---
config:
look: 'handDrawn'
---
graph LR
subgraph t_list
head[head]
end
head --> A["[10 | @]"] --> B["[20 | @]"] --> C["[30 | NULL]"]
style t_list fill:#ecf98f
style head fill:#FFE6E6
style A fill:#E6F3FF
style B fill:#E6F3FF
style C fill:#E6F3FF
---
config:
look: 'handDrawn'
---
graph LR
subgraph t_list
head[head]
end
head --> A["[10 | @]"] --> B["[20 | @]"] --> C["[30 | NULL]"]
N["[40 | NULL]"]
style t_list fill:#ecf98f
style head fill:#FFE6E6
style N fill:#90EE90
style A fill:#E6F3FF
style B fill:#E6F3FF
style C fill:#E6F3FF
---
config:
look: 'handDrawn'
---
graph LR
subgraph t_list
head[head]
end
head --> A["[10 | @]"] --> B["[20 | @]"] --> C["[30 | NULL]"]
N["[40 | NULL]"]
cur -.-> C
style t_list fill:#ecf98f
style head fill:#FFE6E6
style cur fill:#FFE6E6
style N fill:#90EE90
style A fill:#E6F3FF
style B fill:#E6F3FF
style C fill:#E6F3FF
---
config:
look: 'handDrawn'
---
graph LR
subgraph t_list
head[head]
end
head --> A["[10 | @]"] --> B["[20 | @]"] --> C["[30 | @]"] --> N["[40 | NULL]"]
style t_list fill:#ecf98f
style head fill:#FFE6E6
style N fill:#90EE90
style A fill:#E6F3FF
style B fill:#E6F3FF
style C fill:#E6F3FF
void insert_at_tail(t_list *list, int value) {
t_cell * new_cell = create_cell(value);
// Cas 1 : Liste vide
if (list->head == NULL) {
list->head = new_cell;
return;
}
// Cas 2 : Parcourir jusqu'à la dernière cellule
t_cell * cur = list->head;
while (cur->next != NULL) {
cur = cur->next;
}
// Lier la dernière cellule à la nouvelle cellule
cur->next = new_cell;
}Il existe plusieurs cas à gérer lors de la suppression d’une cellule contenant une valeur donnée :
next---
config:
look: 'handDrawn'
---
graph LR
subgraph t_list
head[head]
end
head --> A["[10 | @]"] --> B["[20 | @]"] --> C["[30 | NULL]"]
style t_list fill:#ecf98f
style head fill:#FFE6E6
style A fill:#E6F3FF
style B fill:#FFD1D1
style C fill:#E6F3FF
---
config:
look: 'handDrawn'
---
graph LR
subgraph t_list
head[head]
end
head --> A["[10 | @]"] --> B["[20 | @]"] --> C["[30 | NULL]"]
prev -.-> A
cur -.-> B
style t_list fill:#ecf98f
style head fill:#FFE6E6
style prev fill:#FFE6E6
style cur fill:#FFE6E6
style A fill:#E6F3FF
style B fill:#FFD1D1
style C fill:#E6F3FF
---
config:
look: 'handDrawn'
---
graph LR
subgraph t_list
head[head]
end
head --> A["[10 | @]"] --> C["[30 | NULL]"]
cur -.-> B["[20 | @]"]
B --> C
prev -.-> A
style t_list fill:#ecf98f
style head fill:#FFE6E6
style cur fill:#FFE6E6
style prev fill:#FFE6E6
style A fill:#E6F3FF
style C fill:#E6F3FF
---
config:
look: 'handDrawn'
---
graph LR
subgraph t_list
head[head]
end
head --> A["[10 | @]"] --> C["[30 | NULL]"]
cur -.-> B["[20 | NULL]"]
prev -.-> A
style t_list fill:#ecf98f
style head fill:#FFE6E6
style cur fill:#FFE6E6
style prev fill:#FFE6E6
style A fill:#E6F3FF
style C fill:#E6F3FF
void delete_cell(t_list *list, int value) {
// Cas 1 : Liste vide
if (list->head == NULL) {
printf("Liste vide, rien à supprimer\n");
return;
}
// Cas 2 : L'élément à supprimer est en tête
if (list->head->value == value) {
t_cell *temp = list->head;
list->head = list->head->next;
free(temp);
return;
}
// Cas 3 : L'élément est au milieu ou en fin
t_cell *cur = list->head;
t_cell *prev = NULL;
while (cur != NULL && cur->value != value) {
prev = cur;
cur = cur->next;
}
// Élément non trouvé
if (cur == NULL) {
printf("Élément %d non trouvé\n", value);
return;
}
// Supprimer la cellule
prev->next = cur->next;
cur->next = NULL;
free(cur);
}Dans ce cas là que retourne-t-on si l’élément n’est pas trouvé ?
On peut retourner NULL pour indiquer que l’élément n’a pas été trouvé dans la liste.
cur à la tête de la listecur n’est pas NULL :
tempcur vers la cellule suivantetempNULL pour indiquer qu’elle est vide| Opération | Tableau | Liste chaînée |
|---|---|---|
| Accès à un élément | Direct via l’indice | Nécessite un parcours |
| Insertion en tête | Décalage nécessaire (vers la droite) | Simple modification de pointeurs |
| Insertion en fin | Si espace disponible | Parcours jusqu’à la fin |
| Suppression | Décalage nécessaire | Recherche nécessaire |
| Taille | Fixe | Dynamique |
| Mémoire | Contigu, peut gaspiller | Virtuellement illimité |
// ❌ ERREUR : Perte de la tête (si on passait une copie)
void wrong_function(t_list list) {
t_cell * new_cell = create_cell(5);
list.head = new_cell; // Cette modification ne sera pas visible à l'extérieur !
}
// ✅ CORRECT : Passer un pointeur vers la structure
void correct_function(t_list * list) {
t_cell *new_cell = create_cell(5);
list->head = new_cell; // Modification visible à l'extérieur
}// ❌ ERREUR : Fuite mémoire
void delete_wrong(t_list * list) {
list->head = list->head->next; // On perd l'accès à la première cellule !
}
// ✅ CORRECT : Libérer la mémoire
void delete_correct(t_list * list) {
t_cell *temp = list->head;
list->head = list->head->next;
free(temp); // Libération de la mémoire
}NULL avant de le déréférencermallochead, cur, next, prevMerci pour votre attention !

XTI102 - Listes chaînées