Blog Alphorm Logo de blog informatique spécialisé en technologie et solutions IT
  • Développement
  • 3D et Animation
  • Cybersécurité
  • Infrastructure
  • Virtualisation
  • Réseaux
  • Bureautique
  • BDD
En cours de lecture : Les Listes Chaînées en C: Concepts et Applications
Agrandisseur de policeAa
Blog AlphormBlog Alphorm
  • Développement
  • 3D et Animation
  • Cybersécurité
  • Infrastructure
  • Virtualisation
  • Réseaux
  • Bureautique
  • BDD
Search
  • Développement
  • 3D et Animation
  • Cybersécurité
  • Infrastructure
  • Virtualisation
  • Réseaux
  • Bureautique
  • BDD
Suivez-nous
© Alphorm 2024 - Tous droits réservés
Développement

Les Listes Chaînées en C: Concepts et Applications

L'Équipe Alphorm Par L'Équipe Alphorm 18 janvier 2025
Partager
Partager

Les structures de données statiques comme les tableaux peuvent limiter la flexibilité des applications.

Ces limitations entraînent des inefficacités lors des insertions et suppressions fréquentes.

Les listes chaînées offrent une alternative dynamique, permettant des modifications efficaces et flexibles des données.

Table de matière
Introduction aux Listes Chaînées en CDéfinir les Listes Chaînées et leur UtilitéStructures de Données : ComparatifOpérations sur Listes Chaînées en CConclusion sur les Listes Chaînées en CFAQConclusion

Formation Le langage C : Acquérir les fondamentaux

Développez votre expertise en C et ouvrez la voie à des projets passionnants.

Découvrir cette formation

Introduction aux Listes Chaînées en C

Les listes chaînées sont des structures de données dynamiques qui permettent une gestion flexible des éléments. Contrairement aux tableaux qui ont une taille fixe, les listes chaînées peuvent croître et rétrécir dynamiquement en fonction des besoins de l’application. Elles sont particulièrement utiles lorsque vous devez effectuer des insertions et suppressions fréquentes, car elles offrent une performance supérieure à celle des tableaux pour ces opérations. Ce chapitre explore les concepts fondamentaux des listes chaînées, leur implémentation en C, et discute de leurs avantages et inconvénients par rapport à d’autres structures de données.

Définir les Listes Chaînées et leur Utilité

Liste Chaînée Simple :

Une liste chaînée simple est constituée de noeuds où chaque noeud contient une donnée et un pointeur vers le noeud suivant.

Diagramme de listes chaînées en C

Chaque nœud contient deux éléments :

  • Data :La donnée stockée.
  • P_s1 :Un pointeur vers le nœud suivant.

Exemple du code

				
					
#include <stdio.h>
#include <stdlib.h>   // Pour malloc
struct s1 {
  char *nom;
  char *prenom;
  int age;
  struct s1 *p_s1;
} s;
int main() {
  struct s1 *p_s = NULL;
  p_s = (struct s1 *)malloc(sizeof(struct s1));
  if (p_s == NULL) {
    printf("Échec de l'allocation de mémoire.\n");
    return 1;
  }
  // Initialisation des membres de la structure
  p_s->nom = "Dupont";
  p_s->prenom = "Jean";
  p_s->age = 30;
  p_s->p_s1 = NULL; // Pas de lien vers une autre structure
  // Affichage des résultats
  printf("Nom: %s\n", p_s->nom);
  printf("Prénom: %s\n", p_s->prenom);
  printf("Âge: %d\n", p_s->age);
  // Libérer la mémoire allouée
  free(p_s);
  return 0;
}

				
			
  • Exemple d’exécution de code sur Eclipse
Sortie texte dans Eclipse affichant des données personnelles

Points à noter :

  • L’allocation dynamique permet de créer une structure à l’exécution sans définir une taille fixe à la compilation.
  • La bonne pratique est de libérer la mémoire allouée avec free pour éviter les fuites de mémoire.

Liste Chaînée Double :

Les listes chaînées doubles possèdent deux pointeurs dans chaque noeud : un vers le noeud suivant et un vers le noeud précédent.

Diagramme de listes chaînées en C

Chaque nœud contient trois éléments :

  • P_nexts1 :Un pointeur vers le nœud suivant.
  • Data :La donnée stockée.
  • P_prevs1 :Un pointeur vers le nœud précédent.

Exemple du code

				
					
#include <stdio.h>
#include <stdlib.h>   // Pour malloc
struct s1 {
  char *nom;
  char *prenom;
  int age;
  struct s1 *p_nexts1;
    // Pointeur vers la structure suivante
      struct s1 *p_prevs1;
    // Pointeur vers la structure précédente
} s;
int main() {
  struct s1 *p_s = NULL;
  // Allocation de mémoire pour une structure s1
  p_s = (struct s1 *)malloc(sizeof(struct s1));
  if (p_s == NULL) {
    printf("Échec de l'allocation de mémoire.\n");
    return 1;
  }
  // Initialisation des membres de la structure
  p_s->nom = "Martin";
  p_s->prenom = "Luc";
  p_s->age = 45;
  p_s->p_nexts1 = NULL;
    // Pas de structure suivante pour l'instant
      p_s->p_prevs1 = NULL;
    // Pas de structure précédente pour l'instant
      // Affichage des résultats
      printf("Nom: %s\n", p_s->nom);
  printf("Prénom: %s\n", p_s->prenom);
  printf("Âge: %d\n", p_s->age);
  printf("Pointeur vers la structure suivante: %p\n", (void *)p_s->p_nexts1);
  printf("Pointeur vers la structure précédente: %p\n", (void *)p_s->p_prevs1);
  // Libération de la mémoire allouée
  free(p_s);
  return 0;
}

				
			
  • Exemple d’exécution de code sur Eclipse
Exemple de code de liste chaînée en C++

Points à noter :

  • (0000000) ou NULL est affiché pour les pointeurs non initialisés (ici p_nexts1 et p_prevs1).
  • Comme précédemment, la mémoire allouée est libérée pour éviter les fuites de mémoire.
  • Le code met en place une base pour gérer une liste chaînée double, mais ici, il ne s’agit que d’une seule structure.

Structures de Données : Comparatif

Les tableaux et les listes chaînées sont deux structures de données fondamentales avec des caractéristiques distinctes.

  • Le tableau suivant présente la Comparaison entre un tableaux et une liste des chaînées :
Critère
Tableau
Liste Chaînée Simple
Taille
Fixe
Dynamique
Accès aux éléments
Accès direct par indice
Accès séquentiel
Insertion/Suppression
Coûteux (déplacement)
Efficace (modification des pointeurs)
Allocation mémoire
Continue (contiguë)
Fragmentée (non contiguë)
  • Le tableau suivant présente la Comparaison entre une liste des chaînée simples et double :
Critère
Liste Chaînée Simple
Liste Chaînée Double
Pointeurs
1 (vers le suivant)
2 (vers le suivant et le précédent)
Parcours
Unidirectionnel
Bidirectionnel
Complexité
Moins complexe
Plus complexe (gestion des deux pointeurs)

Opérations sur Listes Chaînées en C

Les listes chaînées permettent d’effectuer plusieurs opérations courantes telles que l’insertion d’un élément au début, à la fin, ou au milieu de la liste, ainsi que la suppression d’un élément. Chacune de ces opérations présente des caractéristiques spécifiques et une complexité en termes de gestion des pointeurs.

Insertion au Début :

L’insertion au début d’une liste chaînée consiste à ajouter un nouvel élément juste avant le premier noeud existant. Cette opération est très rapide car elle ne nécessite pas de parcourir toute la liste.

Étapes :

  • Créer un nouveau noeud.
  • Assigner la donnée au nouveau noeud.
  • Faire pointer le Next du nouveau noeud vers l’ancien premier noeud (l’ancien début de liste).
  • Mettre à jour le pointeur head (tête de liste) pour qu’il pointe vers ce nouveau noeud.

Complexité : O(1) – La complexité est constante car l’opération ne nécessite qu’une modification locale des pointeurs.

Diagramme de listes doublement chaînées en C

Fonction d’insertion au Début d’une Liste Chaînée Simple :

				
					
void insertAtHead(Node **head, int newData) {
  Node *newNode = (Node *)malloc(sizeof(Node));
  newNode->data = newData;
  newNode->next = *head;
  *head = newNode;
}

				
			
  • Exemple d’exécution de code sur Eclipse
Exemple d'insertion dans une liste chaînée en C

Explication des résultats :

  • Avant la première insertion de 10, la liste est vide, donc le message « La liste est vide. » est affiché.
  • Après l’insertion de 10, la liste contient un seul élément :10 -> NULL.
  • Avant l’insertion de 20 , la liste contient 10 -> NULL, et après l’insertion , elle devient 20 -> 10 -> NULL.
  • Avant l’insertion de 30 , la liste contient 20 -> 10 -> NULL, et après l’insertion , elle devient 30 -> 20 -> 10 -> NULL.

Ainsi, chaque insertion au début de la liste met à jour l’état de la liste en ajoutant un nouvel élément en tête.

Insertion à la Fin :

L’insertion à la fin de la liste chaînée consiste à ajouter un nouvel élément après le dernier noeud. Contrairement à l’insertion au début, elle nécessite de parcourir toute la liste pour atteindre le dernier élément.

Étapes :

  • Créer un nouveau noeud.
  • Assigner la donnée au nouveau noeud.
  • Parcourir la liste jusqu’à trouver le dernier noeud (celui dont le next est NULL).
  • Faire pointer le next du dernier noeud vers le nouveau noeud.
  • Le next du nouveau noeud doit pointer vers NULL (indiquant la fin de la liste).

Complexité : O(n) – La complexité est linéaire car il faut parcourir la liste jusqu’à la fin.

Représentation d'une liste doublement chaînée
  • Fonction d’insertion à la Fin d’une Liste Chaînée Simple
				
					
void insertAtEnd(Node **head, int newData) {
  Node *newNode = (Node *)malloc(sizeof(Node));
  Node *last = *head;
  newNode->data = newData;
  newNode->next = NULL;
  if (*head == NULL) {
    *head = newNode;
    return;
  }
  while (last->next != NULL) {
    last = last->next;
  }
  last->next = newNode;
}

				
			
  • Exemple d’exécution de code sur Eclipse
Affichage de l'insertion dans une liste chaînée

Explication des résultats :

  • Avant la première insertion de 10, la liste est vide, donc le message « La liste est vide. » est affiché.
  • Après l’insertion de 10, la liste contient un seul élément :10 -> NULL.
  • Avant l’insertion de 20 , la liste contient 10 -> NULL, et après l’insertion , elle devient 10 -> 20 -> NULL.
  • Avant l’insertion de 30 , la liste contient 10 -> 20 -> NULL, et après l’insertion , elle devient 10 -> 20 -> 30 -> NULL.
Astuce Pratique : Pour insérer un nœud à la fin de la liste, si vous stockez également un pointeur vers le dernier nœud de la liste, vous pouvez éviter d’itérer à travers toute la liste à chaque insertion. Cela rend l’insertion plus efficace (O(1) au lieu de O(n)).

Insertion au Milieu :

L’insertion au milieu d’une liste chaînée consiste à insérer un nouveau noeud après un noeud donné, c’est-à-dire à une position spécifique dans la liste. Cela nécessite généralement d’abord de localiser le noeud après lequel l’insertion doit avoir lieu.

Étapes :

  • Créer un nouveau noeud.
  • Assigner la donnée au nouveau noeud.
  • Parcourir la liste pour atteindre le noeud après lequel l’insertion doit avoir lieu.
  • Faire pointer le next du nouveau noeud vers le noeud suivant celui où l’insertion se produit.
  • Faire pointer le next du noeud actuel (celui où l’insertion a lieu) vers le nouveau noeud.

Complexité : O(n) – La complexité est linéaire car il faut parcourir la liste jusqu’à l’emplacement souhaité.

Diagramme de liste chaînée en C avec pointeurs

Fonction d’insertion au Milieu d’une Liste Chaînée Simple

				
					
void insertAfter(Node *prevNode, int newData) {
  if (prevNode == NULL)
    return;
  Node *newNode = (Node *)malloc(sizeof(Node));
  newNode->data = newData;
  newNode->next = prevNode->next;
  prevNode->next = newNode;
}

				
			
  • Exemple d’exécution de code sur Eclipse
Exemple de liste chaînée en C avec insertion

Explication des résultats :

  • Initialement , la liste est 10 -> 20 -> 30 -> NULL.
  • Avant l’insertion de 15 après le nœud 10, la liste est encore 10 -> 20 -> 30 -> NULL.
  • Après l’insertion de 15 après le nœud 10, la liste devient 10 -> 15 -> 20 -> 30 -> NULL.

Suppression :

La suppression d’un noeud implique de supprimer un élément de la liste et de réajuster les pointeurs pour ne pas briser la chaîne de noeuds. Selon la position du noeud à supprimer (début, milieu ou fin), l’opération diffère légèrement.

Étapes :

  • Identifier le noeud à supprimer.
  • Si le noeud est le premier, mettre à jour le pointeur head pour qu’il pointe vers le noeud suivant.
  • Si le noeud est au milieu ou à la fin, parcourir la liste pour trouver le noeud précédent celui à supprimer.
  • Faire pointer le next du noeud précédent vers le noeud suivant celui à supprimer.
  • Libérer la mémoire du noeud supprimé.

Complexité : O(n) – La complexité est linéaire car il faut parcourir la liste pour localiser le noeud à supprimer.

Diagramme de listes chaînées en C

Code de Suppression d’un Noeud dans une Liste Chaînée Simple

				
					
void deleteNode(Node **head, int key) {
  Node *temp = *head, *prev = NULL;
  if (temp != NULL && temp->data == key) {
    *head = temp->next;
    free(temp);
    return;
  }
  while (temp != NULL && temp->data != key) {
    prev = temp;
    temp = temp->next;
  }
  if (temp == NULL)
    return;
  prev->next = temp->next;
  free(temp);
}

				
			
  • Exemple d’exécution de code sur Eclipse
Exemple suppression noeuds liste chaînée C

Explication des résultats :

  • Initialement , la liste est 10 -> 20 -> 30 -> 40 -> NULL.
  • Avant la suppression de 20 , la liste est 10 -> 20 -> 30 -> 40 -> NULL. Après suppression, elle devient 10 -> 30 -> 40 -> NULL.
  • Lorsqu’on essaie de supprimer un nœud avec la clé 50 , qui n’existe pas, un message « Le nœud avec la clé 50 n’a pas été trouvé. » est affiché.
Erreur de suppression d’un Nœud Inexistant : : Il est important de vérifier que le nœud à supprimer existe avant d’essayer de le supprimer. Si vous essayez de supprimer un nœud avec une clé qui n’existe pas dans la liste, la fonction doit simplement retourner sans effectuer de suppression.

Démonstration Pratique

Voici un code qui effectue à la fois l’insertion et la suppression dans une liste chaînée. L’idée est d’insérer un nouveau nœud dans la liste et de supprimer simultanément un nœud avec une clé spécifique, si ce dernier existe.

				
					
#include <stdio.h>
#include <stdlib.h>
// Définition de la structure Node
typedef struct Node {
  int data;
  struct Node *next;
} Node;
void printList(Node *node) {
  if (node == NULL) {
    printf("La liste est vide.\n");
    return;
  }
  while (node != NULL) {
    printf("%d -> ", node->data);
    node = node->next;
  }
  printf("NULL\n");
}
// Fonction pour insérer un nouveau nœud au début de la liste
void insertAtHead(Node **head, int newData) {
  Node *newNode = (Node *)malloc(sizeof(Node));
  newNode->data = newData;
  newNode->next = *head;
  *head = newNode;
}
// Fonction pour supprimer un nœud ayant une clé spécifique
void deleteNode(Node **head, int key) {
  Node *temp = *head, *prev = NULL;
  // Si le nœud à supprimer est le premier nœud
  if (temp != NULL && temp->data == key) {
    *head = temp->next;
      // Change le pointeur head
        free(temp);
      // Libère la mémoire
        return;
  }
  // Recherche du nœud à supprimer
  while (temp != NULL && temp->data != key) {
    prev = temp;
    temp = temp->next;
  }
  // Si le nœud n'a pas été trouvé dans la liste
  if (temp == NULL)
    return;
  // Détache le nœud de la liste
  prev->next = temp->next;
  // Libère la mémoire du nœud supprimé
  free(temp);
}
// Fonction pour effectuer l'insertion et la suppression en même temps
void insertAndDelete(Node **head, int insertData, int deleteKey) {
  // Insertion d'un nouveau nœud au début de la liste
  insertAtHead(head, insertData);
  // Affichage de la liste après l'insertion
  printf("Liste chaînée après insertion de %d :\n", insertData);
  printList(*head);
  // Suppression du nœud avec la clé deleteKey
  deleteNode(head, deleteKey);
  // Affichage de la liste après suppression
  printf("Liste chaînée après suppression de %d :\n", deleteKey);
  printList(*head);
}
// Fonction pour afficher les éléments de la liste
int main() {
  // Déclaration d'un pointeur vers la tête de la liste
  Node *head = NULL;
  // Création de quelques nœuds manuellement
  insertAtHead(&head, 30);
  insertAtHead(&head, 20);
  insertAtHead(&head, 10);
  // Affichage initial de la liste
  printf("Liste chaînée initiale :\n");
  printList(head);
  // Insertion et suppression simultanées
  insertAndDelete(&head, 40, 20);
    // Insertion de 40, suppression de 20
      return 0;
}

				
			

Explication :

  • Fonction insertAtHead :

Insère un nouveau nœud au début de la liste chaînée.

  • Fonction deleteNode :

Supprime un nœud ayant une clé spécifique. Si le nœud à supprimer est le premier de la liste, il est détaché et libéré. Sinon, la fonction cherche dans la liste et détache le nœud trouvé avant de libérer la mémoire.

  • Fonction insertAndDelete :

Combine à la fois l’insertion d’un nouveau nœud et la suppression d’un autre nœud avec une clé donnée. Après chaque opération (insertion et suppression), la fonction affiche l’état de la liste chaînée.

  • Affichage :

La fonction printList est utilisée pour afficher l’état de la liste avant et après chaque opération.

Exemple d’exécution de code sur Eclipse

Exécution C sur liste chaînée avec insertion et suppression

Conclusion sur les Listes Chaînées en C

Les listes chaînées sont des structures de données puissantes offrant une grande flexibilité pour gérer des collections dynamiques d’éléments. Bien qu’elles présentent certains inconvénients, comme un accès plus lent aux éléments, elles sont très efficaces pour les opérations d’insertion et de suppression. Comprendre leur fonctionnement et leur implémentation en C vous permettra de choisir la meilleure structure de données pour vos besoins en programmation.

Formez-vous gratuitement avec Alphorm !

Maîtrisez les compétences clés en IT grâce à nos formations gratuites et accélérez votre carrière dès aujourd'hui.

Démarrer gratuitement
illustration processus de paiement en ligne avec étapes claires et convivialité

FAQ

Qu'est-ce qu'une liste chaînée simple ?
Une liste chaînée simple est une structure de données où chaque élément, ou ‘nœud’, contient une donnée et un pointeur vers le nœud suivant. Cela crée une chaîne de nœuds reliés entre eux, permettant une gestion dynamique des éléments. Les listes chaînées simples sont idéales pour les situations où la taille de la collection de données peut changer fréquemment, car elles permettent des insertions et suppressions efficaces sans nécessiter de déplacement des autres éléments, contrairement aux tableaux.
Comment fonctionne une liste chaînée double ?
Une liste chaînée double est une extension de la liste chaînée simple, où chaque nœud contient deux pointeurs : un vers le nœud suivant et un vers le nœud précédent. Cette structure bidirectionnelle facilite la navigation dans les deux sens, ce qui est particulièrement utile pour certaines applications nécessitant un accès en arrière. Bien que plus complexe à gérer en termes de mémoire et de pointeurs, elle offre une flexibilité accrue par rapport aux listes chaînées simples.
Quels sont les avantages des listes chaînées par rapport aux tableaux ?
Les listes chaînées offrent une gestion dynamique de la mémoire, ce qui permet des insertions et suppressions rapides sans nécessiter de réallocation ou de déplacement des éléments, contrairement aux tableaux qui ont une taille fixe. Cette flexibilité est particulièrement bénéfique dans les applications nécessitant une gestion fréquente des éléments. Cependant, les listes chaînées nécessitent un accès séquentiel, ce qui peut être plus lent que l’accès direct par indice proposé par les tableaux.
Comment insérer un élément dans une liste chaînée en C ?
Pour insérer un élément dans une liste chaînée en C, vous devez créer un nouveau nœud, initialiser sa donnée, et ajuster les pointeurs pour l’intégrer à la liste. Par exemple, pour une insertion au début, le nouveau nœud pointera vers l’ancien premier nœud, et le pointeur tête de liste sera mis à jour pour pointer vers ce nouveau nœud. Les insertions à la fin ou au milieu nécessitent de parcourir la liste pour atteindre la position souhaitée avant d’ajuster les pointeurs.
Comment supprimer un élément d'une liste chaînée en C ?
La suppression d’un élément d’une liste chaînée en C implique de localiser le nœud à supprimer, ajuster les pointeurs pour contourner ce nœud, et libérer la mémoire qu’il occupait. Si le nœud est en tête, le pointeur tête est mis à jour pour pointer vers le nœud suivant. Pour les nœuds au milieu ou à la fin, il faut parcourir la liste pour trouver le nœud précédent et ajuster son pointeur pour sauter le nœud à supprimer.

Conclusion

Les listes chaînées offrent une flexibilité inégalée pour la gestion dynamique des données. Quelles autres structures de données pourraient compléter vos besoins en programmation?

ÉTIQUETÉ : Langage C
Facebook
Twitter
LinkedIn
Email
WhatsApp
Par L'Équipe Alphorm
Démocratiser la Connaissance Informatique pour Tous !
Suivre :
L'Équipe Alphorm, c'est la démocratisation de la connaissance informatique. Passionnés et dévoués, nous sommes là pour vous guider vers le succès en rendant la technologie accessible à tous. Rejoignez notre aventure d'apprentissage et de partage. Avec nous, le savoir IT devient une ressource inspirante et ouverte à tous dans un monde numérique en constante évolution.

Derniers Articles

  • Techniques pour gérer les fichiers texte en C#
  • Créer et lire un fichier CSV avec C#
  • JSON : Comprendre et Utiliser Efficacement
  • Créer une Base SQLite dans C#
  • Lecture des données SQLite simplifiée
Laisser un commentaire Laisser un commentaire

Laisser un commentaire Annuler la réponse

Vous devez vous connecter pour publier un commentaire.

Blog Alphorm
  • Développement
  • 3D et Animation
  • Cybersécurité
  • Infrastructure
  • Virtualisation
  • Réseaux
  • Bureautique
  • BDD
En cours de lecture : Les Listes Chaînées en C: Concepts et Applications

© Alphorm - Tous droits réservés