Programmation et structures de données 1MI TYPE STRUCTURÉ Il est souvent pratique de regrouper logiquement plusieurs variablesen une seule variable composée. On parle alors de structure oud’enregistrement. Retenir • Un type enregistrement ou type structuré est un type T de variable v obtenu en juxtaposant plusieurs variables v1; v2; … ayant chacune un type T1;T2; … • les différentes variables vi sont appelées champs de v elles sont repérées par un identificateur de champ • si v est une variable de type structuré T possédant le champch, alors la variable v:ch est une variable comme les autres :(type, adresse, valeur) Les structures permettent de rassembler des valeurs de type différent. Par exemple, pour uneadresse, on a besoin du numéro (int) et du nom de la rue (char). 1. Déclaration En algorithmique En langage C nom_de_type= structure: nom_du_champ_1 : type_du_champ_1; nom_du_champ_2 : type_du_champ_2; ... Déclaration de variable structurée : v: nom_de_type; structnom_de_struct { nom_du_champ_1 : type_du_champ_1; nom_du_champ_2 : type_du_champ_2; ... }; Déclaration de variable structurée : structnom_de_struct v; Ou avec une définition de type : typedefstructnom_de_structnom_type; nom_type v; Chaque élément déclaré à l’intérieur de la structure est appelé un champ. Le nom donné à la structure est appelé étiquette de structure. Ici, on a en fait déclaré un type de structure, pas une variable. On déclare les variables associées à une structure de cette manière : struct adresse chez_pierre ,chez_julie ; Car si la structure d’une adresse est toujours la même (numéro et nom de la rue), chez_pierreetchez_julie, qui sont des structadressedifférentes, n’habitent pas au même endroit. On peut initialiser une structure lors de sa déclaration : struct adresse chez_pierre={ 15 , "rue_Dugommier" } ; Exemple de déclaration de type structuré typedef structs_date { charnom_jour[9]; // lundi, mardi, ..., dimanche intnum_jour; // 1, 2, ..., 31 int mois; // 1, 2, ..., 12 intannee; } 16 Programmation et structures de données 1MI typedef structs_date date = {"vendredi", 21, 10, 2011}; 2. Manipulation On accède aux données contenues dans les champs d’une structure et faisant suivre le nom de la strucure par un point "." et le nom du champ voulu : chez_julie.numero=19 ; strcpy(chez_julie.rue,"avenue Pasteur") ; Si 2 structures ont le même type, on peut effectuer : chez_pierre=chez_julie ; /* Pierre a emménagé chez Julie ! */ Mais on ne peut pas comparer 2 structures (avec == ou !=). Rem : On suppose déclarées des variables v;w d’un type structuré. On dispose donc de variablesv.nom_du_champ_ide type type_du_champ_i Toute opération valide sur une variable de type type_du_champ_iest valide sur v.nom_du_champ_i. De plus, l’affectation v = w est valide. Elle est équivalente auxaffectations : v.nom_du_champ_1 = w.nom_du_champ_1 v.nom_du_champ_2 = w.nom_du_champ_2 ………. 3. Tableau de structure On déclare un tableau de structure de la même façon qu’un tableau de variables simples : le nombre d’éléments est précisé entre crochets. struct adresse pers[100] ; Cette déclaration nécessite que la structure adresseait déjà été déclarée avant. persest alors un tableau dont chaque élément est une structure de type adresse. Et pers[i].ruefait référence au champ "rue" de la iemepersonne de la structure pers. Exemple #include<stdio.h> typedef struct point { double abs; double ord; }; intmain() { point p[10]; int i; p[0].ord = 0; p[0].abs = 1; for(i = 1 ; i < 10 ; i++) { p[i].ord = p[i - 1].ord + 1.; p[i].abs = p[i - 1].abs + 2.; } for(i = 0 ; i < 10 ; i++) { printf("p[%d] = (%f, %f)\n", i, p[i].abs, p[i].ord); } } 17 Programmation et structures de données 1MI 4. Structure de structure On peut utiliser une structure comme champ d’une autre structure. Dans la lignée des exemples précédents, on peut définir une structure adressequi pourra être utilisée dans la structure repertoire. Elle peut également être utilisée dans une autre structure. typedef struct adresse { intnumero ; char rue[50] ; } ; structrepertoire { char nom[20] ; char prenom[20] ; struct adresse maison ; } ; /* déclaration d’un champ structure */ /* de type ’adresse’ appelé ’maison’ */ structrepertoiremonrepertoire[100] ; strcpy(monrepertoire[0].nom,"Cordier") ; strcpy(monrepertoire[0].prenom,"Julie") ; monrepertoire[0].maison.numero = 19 ; strcpy(monrepertoire[0].maison.rue,"avenue_Pasteur") ; strcpy(monrepertoire[1].nom,"Durand") ; strcpy(monrepertoire[1].prenom,"Pierre") ; monrepertoire[1].maison.numero = 15 ; strcpy(monrepertoire[1].maison.rue,"rue_Dugommier") ; Lorsqu’un tableau fait partie des champs d’une structure, on peut accéder aux valeurs de ce tableau par : char initiale ; initiale=monrepertoire[1].prenom[0] ; /* initiale de Pierre */ 18 Programmation et structures de données 1MI Les listes chaînées 1. Définition Une liste chaînée est une structure linéaire qui n'a pas de dimension fixée à sa création. Ses éléments de même type sont éparpillés dans la mémoire et reliés entre eux par des pointeurs. Sa dimension peut être modifiée selon la place disponible en mémoire. La liste est accessible uniquement par sa tête de liste c’est-à-dire son premier élément. Pour les listes chaînées la séquence est mise en œuvre par le pointeur porté par chaque élément qui indique l'emplacement de l'élément suivant. Le dernier élément de la liste ne pointe sur rien (Nil). On accède à un élément de la liste en parcourant les éléments grâce à leurs pointeurs. Chaque élément de la liste contient : - des informations sur l’élément - un pointeur sur un autre élément de la liste, ou un pointeur NULL s’il n’y a pas d’élément suivant. 2. Représentation en mémoire d’une liste Soit la liste chaînée suivante (@ indique que le nombre qui le suit représente une adresse) : Adresses Données @:3 @ : 24 @:8 @ : 31 Voici une liste chainée Pointeurs 24 8 31 Nil En allocation dynamique, les emplacements mémoires sont dispersés en mémoire centrale. L’espace est réservé au fur et à mesure des créations des éléments de la liste. La seule limite étant la taille de la mémoire centrale. Pour accéder au troisième élément de la liste il faut toujours débuter la lecture de la liste par son premier élément dans le pointeur duquel est indiqué la position du deuxième élément. Dans le pointeur du deuxième élément de la liste on trouve la position du troisième élément… Pour ajouter, supprimer ou déplacer un élément il suffit d'allouer une place en mémoire et de mettre à jour les pointeurs des éléments. Il existe différents type de listes chainées : Il existe différents types de listes chaînées : • listes simplement chainées (comme ci-dessus) constituée d'éléments reliés entre eux par des pointeurs. • Liste chaînée ordonnée où l'élément suivant est plus grand que le précédent. L'insertion et la suppression d'élément se font de façon à ce que la liste reste triée. • Liste doublement chaînée où chaque élément dispose non plus d'un mais de deux pointeurs pointant respectivement sur l'élément précédent et l'élément suivant. Ceci permet de lire la liste dans les deux sens, du premier vers le dernier élément ou inversement. • Liste circulaire où le dernier élément pointe sur le premier élément de la liste. S'il s'agit d'une liste doublement chaînée alors de premier élément pointe également sur le dernier. • Les piles 19 Programmation et structures de données 1MI • Les files 3.listes simplement chainées Une liste chaînée simple est composée : d'un ensemble d'éléments tel que chacun : • est rangé en mémoire à une certaine adresse, • contient une donnée (Info), • contient un pointeur, souvent nommé Suivant, qui contient l'adresse de l'élémentsuivant dans la liste, d'une variable, appelée Tête, contenant l'adresse du premier élément de la liste chaînée. Le pointeur du dernier élément contient la valeur Nil. Dans le cas d'une liste vide le pointeur de latête contient la valeur Nil. Une liste est définie par l'adresse de son premier élément. Exemple : er Le 1 élément de la liste vaut 12 à l'adresse 3 (début de la liste chaînée) eme Le 2 élément de la liste vaut 14 à l'adresse 4 (car le pointeur de la cellule d’adresse 3 est égal à 4) eme Le 3 élément de la liste vaut 10 à l'adresse 2 (car le pointeur de la cellule d’adresse 4 est égal à 2) Le 4e élément de la liste vaut 24 à l'adresse 1 (car le pointeur de la cellule d’adresse 2 est égal à 1) Si P a pour valeur 3 Si P a pour valeur 2 P^.Info a pour valeur 12P^.Info a pour valeur 10 P^.Suivant a pour valeur 4 P^.Suivant a pour valeur 1 3.1. Traitements de base d'utilisation d'une liste chaînée simple Il faut commencer par définir un type de variable pour chaque élément de la chaîne. En langagealgorithmique ceci se fait comme suit : Type Element = Structure valeur : entier Suivant : Element Fin structure Type Liste = ^Element Variables Tete, P : Liste Typedefstructelement { Intvaleur; element *Suivant; }; typedef element *Liste; int main() { ListeTete,P; } 20 Programmation et structures de données 1MI Les traitements des listes sont les suivants : Créer une liste. Ajouter un élément. Supprimer un élément. Modifier un élément. Parcourir une liste. Rechercher une valeur dans une liste. 4.LES PILES La pile est une structure de données, qui permet de stocker les données dans l’ordre LIFO (Last In First Out = Dernier Entré Premier Sorti). L’insertion des données se fait donc toujours au début de la liste(sommet de la pile) (i.e. par le haut de la pile), donc le premier élément de la liste est le dernier élément inséré, sa position est donc en haut de la pile. Pour permettre les opérations sur la pile, nous allons sauvegarder certains éléments Le premier élément de la pile, qui se trouve en haut de la pile, va nous permettre de réaliser l'opération de récupération des données situées en haut de la pile. Pour réaliser cela, une autre structure sera utilisée (ce n'est pas obligatoire, des variables peuvent êtreutilisées). Voici sa composition : typedefstruct pile { Element *debut; int taille; }; typefedstruct pile Pile ; Le pointeur début contiendra l'adresse du premier élément de la liste. La variable taille contient le nombre d'éléments. 21 Programmation et structures de données 1MI Quatre opérations abstraites sont dé_nies sur le type Pile : empiler : Pile X E →Pile depiler : Pile →Pile sommet : Pile →E est- vide? : Pile →booleen Le rôle de l'opération empiler est d'ajouter un élément en sommet de pile, celui de dpiler de supprimer le sommet de pile et celui de sommet de renvoyer l'élément en sommet de pile. En_n, l'opérationest - vide? indique si une pile est vide ou pas. 5. LES FILES La File diffère de la Pile dans sa façon de gérer les données. En effet, la file permet de stocker les données dans l’ordre FIFO (First In First Out = Premier Entré Premier Sorti). L’insertion des données se fait aussi par en haut de la File, mais la sortie ne se faut plus par le haut comme pour la Pile mais par le bas. Pour manipuler une File, nous sauvegardons son premier élément, son dernier élément ainsi que sa taille (nombre d’éléments qu’elle contient). Pour réaliser cela, une autre structure sera utilisée (ce n'est pas obligatoire, des variables peuvent être utilisées). Voici sa composition : typedefstructfile{ Element *debut; Element *fin; int taille; }; typefedstruct file File ; Le pointeur début contiendra l'adresse du premier élément de la liste. Le pointeur fin contiendra l'adresse du dernier élément de la liste. La variable taille contient le nombre d'éléments. 22 Programmation et structures de données 1MI Quatre opérations sont dé_nies sur le type File : enfiler : File X E →File defiler : File →File dernier : File →E est- vide? : File →booleen L’Opération enfiler a pour rôle d'ajouter un élément en queue de file, et l'opération defiler supprimel'élément en tête de file. dernier retourne le dernier élément de la file et estvide?indique si une file estvide ou pas. Notez que les signatures de ces opérations sont, au mot «File» près, identiques à celles desopérations du type abstrait Pile. Ce sont bien les axiomes qui vont différencier ces deux types abstrait 6. Manipulation des listes, Piles et files 6.1.Initialisation Nous pouvons modéliser un nœud de la liste liée en utilisant une structure comme Suit: typedef struct node { int val; struct node * next; } node_t; La structure de nœud comporte deux membres: • Les données qui stockent les informations • Un pointeur suivant qui contient l'adresse du prochain noeud. Notez que nous définissons la structure de manière récursive, ce qui est possible dans C. Désignons notre type de nœud node_t. Maintenant, nous pouvons utiliser les nœuds. Créons une variable locale qui pointe vers le premier élément de la liste (appelé tête ou Head (sommet)). node_t * head = NULL; head = (node_t*) malloc (sizeof (node_t)); if (head == NULL) { return 1; } head->val = 0; head->next = NULL; nous pouvons mettre dans le champ val du noeud head la taille de la liste (nombre d’éléments) 6.2.Ajouter un nœud à la fin de la liste chainée Pour itérer sur tous les membres de la liste chainée, nous utilisons un pointeur appelé current. Nous l'établissons pour commencer à partir de la tête, puis à chaque étape, nous avançons le pointeur vers l'élément suivant dans la liste, jusqu'à l’arrivée au dernier élément. void push_fin (node_t *head, int val) { node_t *current = head; while (current->next != NULL) { current = current->next; } /* Nous ajoutons un nouveau élément */ current->next = (node_t*)malloc(sizeof(node_t)); current->next->val = val; current->next->next = NULL; 23 Programmation et structures de données head->val=head->val+1; /* On ajoute 1 à la taile de la liste*/ } Ajouter un nœud au début de la liste void push_debut (node_t *head, int val) { node_t *new_node; new_node = (node_t*)malloc(sizeof(node_t)); new_node->val = val; new_node->next = head->next; head->next = new_node; head->val=head->val+1; /* On ajoute 1 à la taile de la liste*/ } Suppression du dernier nœud de la liste int remove_last (node_t * head) { /* le cas d'un seul élément dans la liste */ if (head->next == NULL) return 0; /* aller vers le dernier élément */ node_t * current = head; while (current->next->next != NULL) { current = current->next; } /* on pointe sur le dernier élément*/ free(current->next); current->next = NULL; head->val=head->val-1; /*On change la taille de la liste par -1*/ return 0; } Suppression du premier nœud de la liste int Pop(node_t* head) { if(head->next == NULL) return 0; node_t *front = head->next; head->next = head->next->next; front->next = NULL; /* is this the last node in the list */ if(front == head) head = NULL; free(front); head->val=head->val-1; /*On change la taille de la liste par -1*/ return 0; } 24 1MI Programmation et structures de données Supprimer un nœud spécifique (par son valeur par exemple) int remove_by_val(node_t *head,int valeur){ node_t *current =head; node_t *temp_node=NULL; while (current->next!=NULL) { if (current->next->val==valeur) { temp_node= current; current->next =temp_node->next->next; } current= current->next; } return 0; } Imprimer la liste void print_list(node_t *head) { node_t * current = head->next; if (head->next==NULL) printf("il n'ya aucun noeud dans la liste \n"); else{ printf("la liste contient %d elements \n",head->val); while (current != NULL) { printf("%d\n", current->val); current = current->next;} } } 25 1MI Programmation et structures de données Programme de test global: #include<stdio.h> #include<stdlib.h> typedef struct node { int val; struct node * next; } node_t; /*Ajout à la fin*/ void push_fin (node_t *head, int val) { node_t *current = head; while (current->next != NULL) { current = current->next; } /* Nous ajoutons un nouveau élément */ current->next = (node_t*)malloc(sizeof(node_t)); current->next->val = val; current->next->next = NULL; head->val=head->val+1; } /*Ajout au début*/ void push_debut (node_t *head, int val) { node_t *new_node; new_node = (node_t*)malloc(sizeof(node_t)); new_node->val = val; new_node->next = head->next; head->next = new_node; head->val=head->val+1; } /*Suppression du dernier élément*/ int remove_last (node_t * head) { /* le cas d'un seul élément dans la liste */ if (head->next == NULL) return 0; /* aller vers le dernier élément */ node_t * current = head; while (current->next->next != NULL) { current = current->next; } /* on pointe sur le dernier élément*/ free(current->next); current->next = NULL; 26 1MI Programmation et structures de données head->val=head->val-1; return 0; } /*Suppression du premier élément*/ int Pop(node_t* head) { if(head->next == NULL) return 0; node_t *front = head->next; head->next = head->next->next; front->next = NULL; /* is this the last node in the list */ if(front == head) head = NULL; free(front); head->val=head->val-1; return 0; } int remove_by_val(node_t *head,int valeur){ node_t *current =head; node_t *temp_node=NULL; while (current->next!=NULL) { if (current->next->val==valeur) { temp_node= current; current->next =temp_node->next->next; } current= current->next; } return 0; } void print_list(node_t *head) { node_t * current = head->next; if (head->next==NULL) printf("il n'ya aucun noeud dans la liste \n"); else{ printf("la liste contient %d elements \n",head->val); while (current != NULL) { printf("%d\n", current->val); current = current->next;} } } int main() { 27 1MI Programmation et structures de données node_t * head = NULL; head = (node_t*) malloc (sizeof (node_t)); head->val = 0; head->next = NULL; push_fin(head,3); print_list(head); printf("****************** \n"); push_fin(head,9); print_list(head); printf("****************** \n"); push_fin(head,10); print_list(head); printf("****************** \n"); push_debut(head,8); print_list(head); printf("****************** \n"); push_debut(head,15); print_list(head); printf("****************** \n"); remove_by_val(head,9); print_list(head); printf("****************** \n"); Pop(head); print_list(head); printf("****************** \n"); Pop(head); print_list(head); printf("****************** \n"); Pop(head); print_list(head); printf("****************** \n"); Pop(head); print_list(head); printf("****************** \n"); Pop(head); print_list(head); } 28 1MI Résultats d’exécution du programme la liste contient 1 elements 3 ****************** la liste contient 2 elements 3 9 ****************** la liste contient 3 elements 3 9 10 ****************** la liste contient 4 elements 8 3 9 10 ****************** la liste contient 5 elements 15 8 3 9 10 ****************** la liste contient 5 elements 15 8 3 10 ****************** la liste contient 4 elements 8 3 10 ****************** la liste contient 3 elements 3 10 ****************** la liste contient 2 elements 10 ****************** il n'ya aucun noeud dans la liste ****************** il n'ya aucun noeud dans la liste