cours10

publicité
INF145 : Dixième cours
Les Structures de Données :
-
Définition :
o Organisation de l’information permettant d’améliorer l’efficacité d’un
algorithme. Une telle structure peut contenir des informations redondantes
-
Caractéristiques les plus courantes :
o Vitesse d’ajout, de retrait et de consultation ;
o Espace mémoire occupée (y a t’il des métadonnées, est-ce que si la
structure est vide celle-ci occupe quand même beaucoup de mémoire ?) ;
o Flexibilité (la taille est-elle limitée par une constante à la compilation ?).
Les Listes Chaînées Simples :
-
Définition : Structure de données où les objets sont arrangés de façon linéaire.
À la différence d’un tableau, où l’ordre linéaire est déterminé par l’indice des
éléments, l’ordre dans une liste chaînée est déterminé par un pointeur dans
chacun des objets. Les listes chaînées fournissent une représentation simple et
flexible pour implanter des ensembles dynamiques et supportent (bien que pas
toujours de façon efficace) les opérations associées à un ensemble dynamique ;
 Consultez l’exposé powerpoint « Listes.pps ».
-
Implantation :
Un « nœud » dans une liste chaînée est une structure (type struct) qui
contient un pointeur à une autre structure du même type, et ainsi de suite :
struct noeud {
int data;
/* Champ de données… */
struct noeud * suivant;
};
Ou encore :
typedef struct noeud
T_noeud;
struct noeud {
int data;
/* Champ de données… */
T_noeud * suivant; /* Pointeur */
};
Déclaration de 3 nœuds de type « T_noeud » :
T_noeud
a, b, c;
1
Initialisation des données (sans faire les liens!) :
a.data = 1;
b.data = 2;
c.data = 3;
a.suivant = b.suivant = c.suivant = NULL;
Résultat :
a
1
b
NULL
2
c
NULL
3
NULL
La valeur NULL sera la valeur spéciale (=0) qui indiquera la fin d’une liste chaînée.
Pour faire la liaison des 3 nœuds ensemble :
a.suivant = &b;
b.suivant = &c;
Résultat :
a
b
1
c
2
Quelle est la valeur de :
3
NULL
a.data ?
a.suivant->data ?
a.suivant->suivant->data ?
Déclarations pour former une liste chaînée simple (ordonnée) :
NOTE : Il est préférable d’utiliser typedef pour la déclaration du type des données.
typedef
int
T_objet;
typedef
struct noeud * T_lien;
struct noeud {
T_objet data;
T_lien suivant;
};
/* champ des données */
/* pointeur au nœud suivant */
Le type « struct noeud » sera employé pour faire l’allocation dynamique (avec malloc)
de la liste un nœud à la fois. Pour créer une liste vide, nous avons seulement besoin
d’un seul pointeur :
T_lien
tete = NULL;
Pour créer le premier nœud de la liste :
tete = (T_lien) malloc(sizeof(struct noeud));
tete->data = 2;
tete->suivant = NULL;
Résultat :
tete
2
NULL
2
//1 noeud
On ajoute un 2ième nœud (à la fin de la liste) :
T_lien
tempo = (T_lien) malloc(sizeof(struct noeud));
tempo->data = 4;
tempo->suivant = NULL;
tete->suivant = tempo;
Résultat :
tete
//faire le lien avec la tête
2
4
NULL
(tempo)
Ajouter un nœud au début de la liste :
Étapes :
1. Créer un nouveau nœud avec « malloc ». Variable « tempo » pointe à ce
nouveau nœud ;
2. Remplir « *tempo » avec les nouvelles données (dans : tempo->data ) ;
3. Initialiser « tempo->suivant » pour que ce pointeur pointe à la « tete » ;
4. Faire : « tete = tempo; » pour déplacer la tête de la liste.
void inserer_au_debut(T_lien *tete, T_objet x)
{ T_lien tempo;
tempo = (T_lien) malloc(sizeof(struct noeud));
if (tempo == NULL) return;
//valider l'allocation dynamique
tempo->data
= x;
tempo->suivant = *tete;
*tete = tempo;
}
 appel de cette fonction : inserer_au_debut(&tete, 1);
Résultat :
tete
1
2
4
NULL
(tempo)
Insérer un nœud dans la liste (au milieu) :
Étapes : insertion après le nœud « Ici ».
1. Localiser l’endroit où on veut insérer le nœud (positionner pointeur « Ici ») ;
2. Créer le nouveau noeud « tempo » avec « malloc » et remplir son contenu ;
3. Initialiser « tempo->suivant = ici->suivant; » ;
4. Initialiser « ici->suivant = tempo; ».
ATTENTION : ne PAS faire l’étape 4 avant l’étape 3 !!!
3
void inserer(T_lien *tete, T_objet x)
{ T_lien ici, next, tempo;
tempo = (T_lien) malloc(sizeof(struct noeud));
if (tempo == NULL) return;
//valider l'allocation dynamique
tempo->data = x;
ici = NULL;
next = *tete;
/* Boucle pour localiser l’endroit où « x » sera insérée */
while ((next != NULL) && (next->data < x)) {
ici = next;
next = next->suivant;
}
if (ici == NULL)
tempo->suivant
*tete = tempo;
}
else {
tempo->suivant
ici->suivant =
}
{
//ajouter la valeur «x» au début!
= *tete;
//insertion du nouveau nœud avec valeur x
= ici->suivant;
tempo;
}
 appel de cette fonction : inserer(&tete, 3);
Résultat :
tete
1
2
3
4
(ici)
(tempo)
(next)
Retirer un nœud de la liste :
Nous aurons besoin de 2 pointeurs : « ici » et « avant ».
Étapes : retrait du nœud qui contient « x ».
1. Faire : ici = *tete; avant = NULL;
2. Tant que (ici ≠ fin de la liste) ET ( *ici ne contient pas « x »)
Faire : avant = ici; ici = ici->suivant;
3. Si (ici ≠ NULL)
//aller à droite
// l’élément « x » à été trouvé
Si (ici == *tete), *tete = ici->suivant;
Sinon,
avant->suivant = ici->suivant;
4. Libérer la mémoire du nœud éliminé : free(ici);
4
NULL
Rechercher un élément dans la liste :
Étapes : recherche de la valeur « x ».
tempo = tete;
1. Initialiser :
2. Tant que (tempo ≠ fin de la liste) ET ( *tempo ne contient pas « x »)
Faire : tempo = tempo->suivant;
3. Si (tempo == NULL)
//fin de la liste!
L’élément n’est pas dans la liste, on renvoie NULL
Sinon, on a trouvé l’élément « x », on renvoie un pointeur à ce nœud.
En C, sous forme de fonction :
T_lien
trouver_element(T_lien tete, T_objet x){
T_lien
tempo = tete;
while ((tempo != NULL) && (tempo->data != x))
tempo = tempo->suivant;
return tempo;
}
Exemple d’appel de la fonction :
T_lien
ici = trouver_element(tete, 3);
if (ici != NULL) { . . . }
Intro_Liste.CPP : Exemple de gestion d'une liste chaînée simple
#include
#include
#include
#include
<stdio.h>
<conio.h>
<stdlib.h>
<time.h>
#define
MAX
10
/********************************************************************/
/*
DEFINITION DES TYPES
*/
/********************************************************************/
typedef int
T_objet;
typedef struct noeud * T_lien;
struct noeud {
T_objet data;
T_lien suivant;
};
/********************************************************************/
/*
Déclarations des fonctions de gestion de liste
*/
/********************************************************************/
void ajouter(T_lien *, T_lien *, T_objet);
int retirer_le_i(const T_lien *, int, T_objet *);
void retirer_du_debut(T_lien *);
5
/********************************************************************/
/*
PROGRAMME PRINCIPAL
*/
/********************************************************************/
int main(void)
{ T_lien tete=NULL, fin; //pointeur sur la tete et la fin de la liste
int
nbelem=0,
//nombre d'éléments de la liste
i, valeur;
srand(time(NULL));
//remplir la liste aléatoirement
for (i=0; i<MAX; i++){
valeur = rand() % 100;
ajouter(&tete, &fin, valeur);
nbelem++;
}
//afficher la liste
for (i=0; i<nbelem; i++){
retirer_le_i(&tete, i, &valeur);
printf("%d --> ", valeur);
}
printf("NULL\n");
//tester quelques requêtes
if (retirer_le_i(&tete, 6, &valeur))
printf("Le #6 est %d\n", valeur);
if (retirer_le_i(&tete, 11, &valeur))
printf("Le #11 est %d\n", valeur);
//effacer la liste
while (nbelem){
retirer_du_debut(&tete);
nbelem--;
}
system("pause");
return 0;
}
/********************************************************************/
/*
DEFINITION DES FONCTIONS
*/
/********************************************************************/
/********************************************************************/
//Fonction qui crée un nouveau noeud avec la valeur "x" et qui va
//placer ce noeud à la fin de la liste reçue en paramètre
void ajouter(T_lien *tete, T_lien *fin, T_objet x)
{ T_lien p, q;
p = (T_lien) malloc(sizeof(struct noeud));
if (p == NULL) return;
//valider l'allocation dynamique
p->data = x;
if (*tete == NULL) {
*tete = *fin = p;
p->suivant = NULL;
}
//si la liste reçue est vide..
//ce premier noeud est la tete ET la fin de liste
6
else {
q = *fin;
p->suivant = NULL;
q->suivant = p;
*fin = p;
//sinon, on va ajouter à la fin
//déplacer fin de liste
}
}
/********************************************************************/
//Fonction qui enlève le PREMIER noeud de la liste reçue en parametre
void retirer_du_debut(T_lien *tete)
{ T_lien p= *tete;
if (*tete != NULL) {
*tete = p->suivant;
free(p);
}
}
/********************************************************************/
//Fonction qui récupère la valeur du noeud #i de la liste
int retirer_le_i(const T_lien *tete, int i, T_objet *val)
{ T_lien ici = *tete; //pointeur temporaire pour traverser la liste
int no=0;
if (ici == NULL){
printf("\nOn ne peut pas retire l'objet #%d: la liste est vide\n\n", i);
return 0;
}
else {
/* Boucle pour retrouver la valeur #i dans la liste */
while ((ici != NULL) && (no < i)) {
ici = ici->suivant;
no++;
}
if (ici == NULL){
printf("\nIl n'y a pas d'item numero %d !!!\n\n", i);
return 0;
}
else {
*val = ici->data;
//on récupère cette valeur
return 1;
}
}
}
7
LISTE.CPP : Gestion d’une liste chaînée ordonnée en ordre DÉCROISSANT.
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
/********* Déclarations des types pour la liste chaînée *********/
typedef int
T_objet;
typedef struct noeud * T_lien;
struct noeud {
T_objet data;
T_lien suivant;
};
/********* Déclarations des fonctions de gestion de liste *********/
void
void
void
void
void
void
void
void
void
creer
afficher
inserer
inserer_au_debut
inserer_a_la_fin
retirer
retirer_du_debut
retirer_de_la_fin
trier(T_lien);
(T_lien *);
(T_lien);
(T_lien *, T_objet);
(T_lien *, T_objet);
(T_lien *, T_objet);
(T_lien *, T_objet);
(T_lien *);
(T_lien *);
int main(void)
{ T_lien tete;
creer(&tete);
inserer_au_debut(&tete, 75);
inserer_a_la_fin(&tete, 5);
inserer(&tete, 25);
retirer(&tete, 40);
retirer(&tete, 41);
retirer_du_debut(&tete);
retirer_de_la_fin(&tete);
trier(tete);
afficher(tete);
afficher(tete);
afficher(tete);
afficher(tete);
afficher(tete);
afficher(tete);
afficher(tete);
afficher(tete);
afficher(tete);
getch();
getch();
getch();
getch();
getch();
getch();
getch();
getch();
getch();
return 0;
}
void creer(T_lien *tete)
{ T_lien p;
int i;
//va créer la liste 50 --> 40 --> 30 --> 20 --> 10 --> NULL
*tete = NULL;
for (i=1; i<=5; i++) {
p = (T_lien) malloc(sizeof(struct noeud));
if (p == NULL)
{ *tete = NULL; return; } //valider l'allocation dynamique
p->data
= 10 * i;
p->suivant = *tete;
*tete = p;
}
}
8
int compter(T_lien tete)
{ T_lien p=tete;
int n=0;
//petite fonction qui compte le nombre d’éléments dans la liste
while (p != NULL)
{ n++; p = p->suivant; }
return n;
}
void afficher(T_lien tete)
{ T_lien p=tete;
//pour afficher la liste du début à la fin
while (p != NULL) {
printf( "%5d", p->data);
p = p->suivant;
}
printf("\n\nLa liste compte %d éléments\n\n\n", compter(tete));
}
/* Fonction qui insère l’objet « x » au début de la liste */
void inserer_au_debut(T_lien *tete, T_objet x)
{ T_lien p;
p = (T_lien) malloc(sizeof(struct noeud));
if (p == NULL)
return;
//valider l'allocation dynamique
p->data
= x;
p->suivant = *tete;
*tete
= p;
}
/* Fonction qui insère l’objet « x » à la fin de la liste */
void inserer_a_la_fin(T_lien *tete, T_objet x)
{ T_lien p, q;
p = (T_lien) malloc(sizeof(struct noeud));
if (p == NULL)
return;
//valider l'allocation dynamique
p->data = x;
if (*tete == NULL) {
*tete
= p;
p->suivant = NULL;
}
else {
q = *tete;
while (q->suivant != NULL)
q = q->suivant;
p->suivant = NULL;
q->suivant = p;
}
}
9
/* Fonction qui insère l’objet « x » en ordre (selon sa valeur) dans la liste */
void inserer(T_lien *tete, T_objet x)
{ T_lien ici, next, p;
p = (T_lien) malloc(sizeof(struct noeud));
if (p == NULL)
return;
//valider l'allocation dynamique
p->data = x;
ici = NULL;
next = *tete;
/* Boucle pour localiser l’endroit où « x » sera insérée */
while ((next != NULL) && (next->data > x)) {
ici = next;
next = next->suivant;
}
if (ici == NULL) {
//insertion du nouveau nœud avec valeur « x »
p->suivant = *tete;
*tete = p;
}
else {
p->suivant = ici->suivant;
ici->suivant = p;
}
}
/* Fonction qui enlève le premier nœud de la liste */
void retirer_du_debut(T_lien *tete)
{ T_lien p= *tete;
if (*tete != NULL)
{ *tete = p->suivant;
free(p); }
}
/* Fonction qui enlève le nœud avec la valeur « x » de la liste */
void retirer(T_lien *tete, T_objet x)
{ T_lien ici= *tete, avant=NULL;
if (ici == NULL)
printf("\nOn ne peut pas retirer l'objet %d: la liste est vide\n\n", x);
else {
while ((ici != NULL) && (ici->data != x)) { //on cherche la valeur « x »
avant = ici;
ici = ici->suivant;
}
if (ici == NULL)
printf("\nIl n'y a pas d'item numéro %d !!!\n\n", x);
else {
if (ici == *tete) *tete = ici->suivant;
else
avant->suivant = ici->suivant;
free(ici);
}
}
}
10
/* Fonction qui enlève le dernier nœud de la liste */
void retirer_de_la_fin(T_lien *tete)
{ T_lien ici= *tete, avant=NULL;
if (ici == NULL)
printf("\nOn ne peut pas retirer d'objet: la liste est vide\n\n");
else {
while (ici->suivant != NULL) {
//on cherche la fin de la liste
avant = ici;
ici = ici->suivant;
}
if (ici == *tete) *tete = NULL;
else
avant->suivant = NULL;
free(ici);
}
}
/* Fonction qui échange deux valeurs (pour le tri) */
void permute(T_objet *a, T_objet *b){
T_objet temp=*a;
*a = *b; *b = temp;
}
/* Fonction qui va trier une liste chaînée avec l'algorithme du Tri par Sélection.
PARAMETRE: liste - pointeur vers la liste à trier
*/
void trier(T_lien liste){
T_lien p=liste, q, min;
/* "p" pointe sur le premier noeud */
while (p->suivant != NULL) {
q = p->suivant;
min = p;
/* on ira jusqu'au dernier noeud */
/* boucle pour trouver la valeur minimale du reste de la liste */
while (q != NULL) {
if (q->data < min->data)
min = q;
q = q->suivant;
}
/* On échange ce minimum avec le .data du noeud pointé par "p" */
permute(&p->data, &min->data);
p = p->suivant;
/* on passe au noeud suivant.. */
}
}
ÉTAT DE LA LISTE APRÈS CHAQUE AJOUT/RETRAIT :
tete  50  40  30  20  10  NULL
tete  75  50  40  30  20  10  NULL
tete  75  50  40  30  20  10  5  NULL
tete  75  50  40  30  25  20  10  5  NULL
tete  75  50  30  25  20  10  5  NULL
tete  50  30  25  20  10  5  NULL
tete  50  30  25  20  10  NULL
tete  10  20  25  30  50  NULL
(après la fonction « trier »)
11
Téléchargement