Vous êtes sur la page 1sur 9

UNIVERSITE ADVENTISTE COSENDAI GESTION ET INFORMATIQUE

L2 Informatique

Cours : Algorithmique 2 Enseignant : Dr Lossan BONDE

Année Académique 2020/2021 Semestre 1

1
Chapitre 2 Tableaux, Pointeurs et Listes Chaînées
Introduction
Les structures (enregistrements), tableaux, et pointeurs sont les ingrédients de base indispensables
pour construire des structures de données complexes. Sans pointeurs, il est impossible de faire de
l'allocation dynamique de mémoire. Les listes chaînées sont les structures de données dynamiques
les plus simples, réalisées grâce à la combinaison de structures et de pointeurs. Pour écrire des
algorithmes et les programmes informatiques qui servent à quelque chose, il est indispensable de
maîtriser le contenu de ce chapitre.
Les structures ayant déjà été étudiées dans le cours de programmation C, nous allons dans ce
chapitre faire un rappel sur les tableaux, et nous focaliser sur les Pointeurs et Listes Chaînées.

2.1 Les tableaux


Un tableau est une collection d'éléments de même type. Les éléments d'un tableau sont stockés de
façon linéaire en mémoire. Les tableaux sont des structures de données linéaires et alloués
statiquement. A la compilation, l'emplacement relatif et la taille mémoire nécessaire au stockage du
tableau est déterminés.

2.1.1 Intérêt des tableaux


Les tableaux sont des structures de données appropriées pour stocker un ensemble d'éléments tous
de même type et dont le nombre peut être facilement estimé à l'avance (avant l'exécution du
programme). De plus les tableaux ont l'avantage de permettre un accès aléatoires aux éléments. En
effet chaque élément du tableau est accessible au moyen d'un ou plusieurs indices (selon la
dimension du tableau).

2.1.2 Les tableaux en C


En C, deux formes de syntaxe permettent de déclarer une variable de type tableau.

1. Forme 1
type_des_elements nom_variable [ int litteral ou constante];
Dans la forme 1, la variable est déclarée mais non initialisée. Parce que les tableaux sont alloués
statiquement (au moment de la compilation), la taille du tableau devra être connue à la compilation.
Cette taille devra être fournie dans la déclaration soit par une valeur entière littérale, ou à l'aide
d'une constante.

2. Forme 2
type_des_elements nom_variable [ ] = {valeur1, valeur2, …, valeurN};
Dans la forme 2, le tableau est déclaré et initialisé. Le premier élément du tableau aura la valeur
valeur1, le second valeur2, et ainsi de suite, et le dernier élément sera valeurN. La taille du tableau
est dans ce cas déterminée par le nombre de valeurs fournies pour l'initialisation.
Les deux fragments de code ci-dessous sont donc équivalentes, elles définissent chacun une variable
ages de type tableau d'entiers courts (short) de taille 4. La déclaration de gauche correspond à la
forme 1, tandis que celle de droite suit la forme 2.

short ages[4]; // déclaration ages[2] = 65;


//initialisation ages[3] = 74;
ages[0] = 23; short ages[]={23, 34, 65, 74};
ages[1] = 34;

2
Représentation Mémoire de la variable ages
En supposant d'une valeur de type short est codée sur 2 octets, la représentation mémoire de la
variable tableau ages est semblable au schéma de la figure 1.

Figure 2.1Représentation mémoire du tableau ages

Le nom d'une variable tableau est équivalent à l'adresse mémoire du tableau ; c'est-à-dire l'adresse
mémoire du premier élément du tableau.

Tableau de dimension 2
Un tableau de dimension 2 est un tableau dans lequel chaque élément est localisé à l'aide de deux
indices (ligne, colonne). L'instruction suivante déclare un tableau 10x5 (une matrice 10x5) ; c'est-à-
dire contenant 10 lignes et 5 colonnes.

int tab[10][5];
Mathématiquement un tel tableau est représenté par la figure 2.

Figure 2.2 Représentation mathématique d'un tableau 10x5

En mémoire, les éléments d'un tableau de dimension 2 sont stockés de façon linéaire en suivant
l'ordre des numéros de lignes, et pour chaque ligne, les éléments sont stockés de la gauche vers la
droite, c'est-à-dire en partant de la première colonne à la dernière. Ainsi pour la déclaration de la
variable tab ci-dessous, l'addresse d'un élément d'indices i, j est calculée comme suit.
Addresse (tab[i][j]) = tab+(i*C+j)*sizeof(type)

3
type tab[L][C] ;

Un tableau de dimension de dimension 2 peut être initialisé à la déclaration, comme le montre le


fragment de code ci-après.
int tab2[ ][ ] = {{0, 3}, {3,1}, {0,10}, {0,0}};

Cette initialisation permet de voir qu'un tableau de dimension 2 peut être perçu comme un tableau
de dimension 1 ou chaque élément (une ligne) est lui-même un tableau de dimension 1 (colonne).

Exercice: Donnez la représentation mathématique du tableau de la variable tab2.


Bien que dans la pratique, on se limite à des tableaux de dimension 2, il est toute fois possible de
créer des tableaux de dimension 3, 4, …, n.

2.1.3 Les tableaux irréguliers


Les tableaux de dimensions 2 présentés ci-dessus sont dits des tableaux réguliers, car toutes les
lignes de ces tableaux ont le même nombre de colonnes. Il arrive que les données stockées dans un
tableau ne correspondent à cette utilisation régulière du tableau. C'est le cas par exemple pour les
matrices diagonales, triangulaires, ...

Les matrices triangulaires


Certaines informations naturellement perçues comme des tableaux (matrices) peuvent ne pas
requérir toutes les cases du tableau. En utilisant un tableau régulier, certaines cases sont remplies
de valeurs nulles. Les exemples ci-dessous illustrent cette situation.

4
Par exemple ci vous utilisez un tableau pour représenter le triangle de Pascal (voir TD N°1), vous
n'utiliserez en réalité que la partie triangulaire basse (inférieur) du tableau.
Matrice triangulaire inférieure
Chaque entre (i,j) de la matrice est localisée dans la représentation linéaire par la position:
1
𝑖(𝑖 + 1) + 𝑗
2
Pour éviter la multiplication dans le calcul (couteux en temps CPU), une table d'accès peut être
construite comme le montre l'exemple ci-dessous:

5
Autre tableau irrégulier (Jagged Arrays)
Il s'agit de tableau où toutes les lignes n'ont pas le même nombre de colonnes; et pour une ligne
donnée, le nombre de colonnes n'est prédictible. Pour accéder aux éléments d'un tel tableau, il faut
impérativement construire une table d'accès (voir exemple ci-dessous).

➢ La première ligne commence à l'indice 0. Etant donné qu'elle comporte 4 éléments, la


seconde ligne commence donc à l'indice 4.
➢ La seconde ligne commence à 4 et comporte 10 éléments, la troisième ligne commencera
donc à 14.
➢ La troisième ligne ne contenant aucun élément, la quatrième ligne commencera à 14.
➢ et ainsi de suite, on construit la table d'accès en suivant l'ordre naturel des éléments.

2.2 Les Pointeurs


Une variable pointeur est une variable dont le contenu est un entier correspondant à une adresse
mémoire. La valeur ou l'objet contenu dans la mémoire à cette adresse est appelé l'objet pointé par
le pointeur.
Le pointeur lui-même est une variable allouée statiquement, mais l'objet pointé est dynamiquement
alloué; c'est-à-dire que l'emplacement ainsi que la taille de l'objet sont déterminés à l'exécution du
programme.

2.2.1 Intérêts des pointeurs


1) permet de définir une structure pouvant contenir un ensemble d'éléments dont le nombre est
difficilement estimable à l'avance.
2) dans un langage comme le C, où les paramètres de fonctions sont passés uniquement par valeur,
le pointeur est le seul moyen permettant à une fonction de modifier ses paramètres.
3) les pointeurs permettent de passer des fonctions comme paramètres de d'autres fonctions.
4) les pointeurs permettent de définir des structures de données non linéaires sont les arbres, les
graphes, ... (voir algo 3).

6
2.2.2 Dangers des pointeurs
1) La mémoire allouée dynamiquement grâce au pointeur doit être libérée manuellement par le
programmeur. En oubliant de faire la libération, on arrive à des fuites de mémoire.
2) le problème des alias: deux variables pointeurs pointant le même emplacement mémoire
deviennent des alias et peuvent ainsi conduire à des modifications non voulues.
3) L'allocation dynamique de mémoire faite à l'aide d'une variable pointeur locale demeure au-delà
de la fonction, quand bien la portée de la variable est limitée à la fonction. La raison de faire cela
devra être de construire le résultat de la fonction. Dans ce cas, la fonction retourne le pointeur
comme résultat.
Il faut donc manipuler les pointeurs avec beaucoup de précautions.

2.2.3 Pointeurs et tableaux


Après l'allocation, une variable pointeur peut être manipulée comme un tableau. De même on peut
accéder aux éléments d'un tableau en utilisant l'arithmétique des pointeurs. En fait, le nom d'une
variable tableau représente l'adresse de début du tableau, par conséquent il peut être utilisé comme
un pointeur.

2.2.4 Pointeurs en C
Lire attentivement et faire tous les exercices des chapitres 11 et 12 du livre « INITIATION A
L’ALGORITHMIQUE ET A LA PROGRAMMATION EN C » (fourni sur la plateforme).

7
2.3 Les Listes Chaînées
L'inconvénient majeure de l'allocation statique avec les tableaux est la gestion médiocre de la
mémoire: risque de sous estimer et obtenir des débordements de mémoire, ou alors mobiliser une
quantité plus que nécessaire. Les listes chaînées permettent de résoudre ce problème.

Les listes chaînées sont des structures de données linéaires permettant de


stocker un ensemble d'éléments; où les éléments sont accessibles grâce à des
pointeurs. Les pointeurs permettent de chaîner les éléments entre eux.

Chaque élément de la liste appelé (nœud) est constitué de données et de pointeur(s). Le ou les
pointeurs contiennent les adresses d'autres éléments de la liste. Les listes chaînes sont alors dites
des structures autoréférentes. L'inconvénient majeur des listes chaînées est que l'accès aux
éléments se fait de façon séquentielle.
Une liste chaînée est identifiée par un pointeur sur le premier élément de la liste.
Les opérations courantes sur les listes chaînées sont : l'insertion, la suppression, et le parcours.

2.3.1 Liste Chaînées Circulaires


A cause du parcours séquentiel des listes chaînées, il n'est pas possible de visiter un élément situé
après l'itérateur de la liste. Pour remédier à cette situation, on peut rendre la liste circulaire. Le
dernier élément pointe sur le premier.

2.3.2 Liste doublement chaînée


Une liste chaînée simple ne peut être parcouru que dans un sens. Pour permettre le parcours dans
les deux sens, il faut doubler le chainage. Chaque nœud contient un pointeur sur l'élément suivant et
un autre pointeur vers son prédécesseur. L'opération de suppression est plus facile à réaliser sur une
liste doublement chaînée.

Suppression dans une liste doublement chaînée.

8
2.3.3 Représentation en C
Liste Simple
typedef struct {
type donnee; // partie données
struct Noeud *next; // pointeur sur le prochain élément
} Noeud;
typedef Noeud* Liste;

Liste doublement chaînée


typedef struct {
type donnee; // partie données
struct Noeud *next; // pointeur sur le prochain élément
struct Noeud *prev; // pointeur sur l'élément précédent
} Noeud;
typedef Noeud* Liste;

Vous aimerez peut-être aussi