Vous êtes sur la page 1sur 67

2.

VECTEURS

2.1. Introduction

2.1.1. Notion d’un vecteur


Au chapitre 1, on a introduit la notion de variable de différents types. Il arrive souvent
que l’on est amené à considérer un ensemble de variables de même nature qu’on peut
accéder :
• soit en les parcourant toutes dans l’ordre (recherche séquentielle),
• soit directement par leur numéro d’ordre (accès direct),
• soit en faisant une recherche de type dichotomique.

Exemple : un dictionnaire encyclopédique.

2.1.2. Définitions formelles


Soient un :
• ensemble quelconque : l’ensemble des valeurs E,
• entier n : nombre d’éléments du vecteur,
• intervalle I de N tel que I = [1..n], avec (n ≥ 0).

Un vecteur est une application V de I dans E ; I est appelé ensemble des indices. 1
2.1.3. Définitions de type

Les éléments de E ne sont pas uniquement des valeurs de type simple


(booléen, caractère, entier, chaîne de caractères), mais peuvent être des
valeurs de type structuré composées de valeurs de type ou structurées.

Exemple
type structure date
entier jour, mois, an;
fin ;

structure personne
nom : chaine20 ;
adresse : chaine40 ;
naissance : date ;
marié : booléen ;
enfants : entier ;
fin; 2
On vient de définir :
• un type date composé de trois nombres entiers (jour, mois et an),
• un type personne composé d’un :
nom (chaîne de 20 caractères au plus),
adresse (chaîne de 40 caractères au plus),
variable naissance de type date,
variable booléenne marié
nombre entier enfants indiquant le nombre d’enfants de cette personne.
On dit que personne comporte cinq champs : nom, adresse, naissance, marié et
enfants. Pour sélectionner un champ on utilise la notation :
nom de variable.nom du champ.
Si pers est une variable de type personne ; on écrira : pers.nom, pers.adresse, pers,
marié, pers.enfants, pour accéder aux différents champs de la variable structurée pers.
On pourra de même accéder à l’ensemble de la date de naissance par pers.naissance,
ou bien à l’année seule par pers.naissance.an.

Les éléments de E pourront aussi eux-mêmes des vecteurs.

3
Ensemble des indices est toujours noté [1..n] dans la suite. Il peut être vide (si n = 0,
[1..n] est vide). Le vecteur est alors dit vide.
L’indice le plus petit (1) est appelé borne inférieure, le plus grand (n) est appelé borne
supérieure.

Taille est le nombre d’éléments de ce vecteur.


Si l’intervalle des indices est [1..n], la taille du vecteur est n.

Notations : un vecteur est noté v[1..n] et mais s’il n’y a pas d’ambiguïté : v.
Un élément du vecteur est noté v[i] avec i  [1..n].
Un vecteur peut être noté par l’ensemble de ses éléments : (v[1], v[2], …, v[n]) ou
(x1,…,xn) si xi Є v[i],
v
1 2 3 4 … n
v[1] v[2] v[3] v[4] ⦁v[n]

Indiçage : soit un vecteur v[1..n], l’opération d’indiçage v[i] délivre la valeur de


l’élément d’indice ou index i du vecteur.
Cette valeur peut être affectée à une variable : A := v[i] ;
On peut ranger une valeur A dans le ième élément par l’affectation : v[i] := A ;

Le résultat de v[i] est indéfini si i  [1..n]. 4


Exemple
1 2 3 4 5 6
v
A B Z A C D

v[1..6] = (‘A’,’B’,’Z’,’A’,’C’,’D’),
v[2] ‘B’,
v[-4] non défini
v[0] non défini
v[4] ‘A’
v[5] ‘C’
v[7] non défini

On remarque l’analogie avec les vecteurs ou tableaux à une dimension


habituellement utilisés en mathématiques.

5
Pour déclarer un vecteur, nou supposerons toujours qu’il existe une définition de type :
t tableau[nmax];
type vecteur tableau[nmax];
L’identificateur t pourra représenter n’importe quel type simple, structuré ou vecteur.

La variable nmax représente le nombre maximum d’éléments possibles pour le type


vecteur. En effet, il faut prévoir le nombre maximum d’éléments du vecteur afin de
réserver la place nécessaire. On dit qu’on a une gestion statique de la mémoire.
Avec cette déclaration, on peut ranger n’importe quel vecteur v[1..n] tel que la borne
supérieure du vecteur n vérifie la relation d’ordre 0 < n ≤ nmax.
1 n nmax
………….

Sous-vecteur
Un sous-vecteur du vecteur v[1..n] est toute restriction de v à un intervalle consécutif
de [1..n]. Un sous-vecteur est noté comme un vecteur.
Exemple
Si v[1..5] = (7, -20, 40, 0, 1), alors les sous-vecteurs v[2..4] et v[1..3] sont formés par
les éléments : (-20, 40, 0) et (7, -20,40). 6
Relation d’ordre
Nous sommes amenés quelquefois à définir une relation d’ordre sur
l’ensemble E des valeurs d’un vecteur. Cette relation est notée, pour des
raisons de simplifications d’écriture, par le symbole <. Quel soit
l’ensemble E. De même, nous utilisons les opérateurs : >, ≤, ≥, =, et ≠.

Autres notations
A < v[1..n] signifie quelque soit j appartenant à [1..n], a < v[j].

On désignera par v[i..j] :


• le sous-vecteur composé des éléments v[i], v[i+1],…,v[j] si i<= j,
• le sous-vecteur vide si i > j.

7
2.2. Algorithmes traitant un seul vecteur
Nous traiterons des problèmes d’accès à une valeur donnée dans un vecteur.

2.2.1. Problème du parcours d’un vecteur


Soit un vecteur v à n éléments. L’idée la plus simple consiste à parcourir le vecteur
dans l’ordre croissant de ses indices. On obtient ainsi le schéma d’énumération
séquentiel « de gauche à droite » du vecteur v.
i := 1 ;
tantque i ≤ n
traiter(v[i]) ;
i := i + 1 ;
finfaire;
Où traiter est une action quelconque sur l’élément v[i].

Ou encore avec l’instruction pour croissante, l’algorithme devient :


pour i := 1 haut n faire
traiter(v[i]) ;
finfaire;

8
Un parcours séquentiel dans l’ordre des indices décroissants, dit ordre
« de droite à gauche ».
Le schéma d’énumération séquentiel des éléments de v.

i := n ;
tantque i ≥ 1 faire
traiter(v[i]) ;
i := i - 1 ;
finfaire;

Qui s’écrit à l’aide d’une instruction pour décroissante.


pour i :=n bas 1 traiter(v[i]) ;
finpour;

Remarque : le parcours d’un vecteur n’est nécessaire que pour


énumérer tous les éléments de ce vecteur, l’accès à un élément n’est
obligatoirement séquentiel. 9
Le parcours récursif d’un vecteur v[i..n] correspond au raisonnement suivant (pour le
parcours de gauche à droite).
•vecteur vide (i >n)
=> le parcours est terminé *
•vecteur non vide (i <= n)
. =>on traite v[i]
=> nouvel appel (récursif) avec v[i+1..n]

Algorithme récursif
procédure parcoursrec(d t v[], d entier i, entier n)
debproc
si i <= n alors
traiter(v[i]) ;
parcoursrec(v[i+1..n]) ;
finsi;
finproc;
On peut représenter la séquence des appels par le schéma suivant :
parcours(v, 1, 3) parcours(v, 2, 3]) parcours(v, 3, 3) parcours(v, 4, 3)
traiter(v[1]) 1 traiter(v[2]) 2 traiter(v[3]) 3 rien
parcours(v, 2, 3) 6 parcours(v, 3, 3) 5 parcours(v, 4, 3) 4

les appels et les retours se font dans l’ordre des numéros. 10


L’algorithme parcousrec est équivalent à l’algorithme itératif.
procédure parcoursiter(d t v[], d entier i, d entier n)
debproc
entier j;
j := i;
tantque j <= n faire
traiter(v[j]);
j := j + 1;
finfaire;
finproc;
Ou encore à :
procédure parcoursiterbis(d t v[], d entier i, d entier n)
debproc
entier j;
pour j := i haut n faire
traiter(v[j]);
finfaire;
finproc;
L’emploi de méthodes récursives pour traiter les parcours simples de vecteurs est lourd et lent à
l’exécution. C’est pourquoi les algorithmes sur les vecteurs seront le plus souvent traités de
manière itérative, en réservant la récursivité aux cas où elle s’impose.
Exemple 1
Soit à faire afficher tous les éléments d’un vecteur, dans l’ordre des indices croissants « de
gauche à droite ». 11
procédure ecrivect1(d t v[], d entier n)
debproc
entier i;
pour i := 1 haut n faire
écrire(v[i]);
finfaire;
finproc;
Ou bien récursivement :
procédure ecrivect1(d t v[], entier i, d entier n)
debproc
si i ≤ n alors
écrire(v[i]);
ecrivect1(v, i+1, n);
finsi;
finproc;
L’appel serait alors de la forme : ecrivect1(v, 1, n); // n doit être initialisée

Exemple 2
Fonction qui calcule la somme des éléments d’un vecteur de nombres entiers (n>0, n est la taille
du vecteur). L’en-tête sera :
entier fonction somme(d entier v[], d entier n)
12
entier fonction somme(d entier v[], d entier n)
debfonc
entier s, i;
s := 0;
pour i := 1 haut n faire
s := s + v[i];
finfaire;
retourner s;
finfonc;

Exercice 2.1. Ecrire une procédure qui affiche les éléments d’un vecteur de nombres
entiers dans l’ordre des indices décroissants (« de droits à gauche »).
Exercice 2.2. Ecrire la même procédure sous forme récursive.
Exercice 2.3. Ecrire une fonction qui calcule le nombre d’occurrences de la valeur val
dans le vecteur v[1..n] composé de nombres entiers.
Exercice 2.4. Ecrire une fonction qui calcule le maximum des éléments d’un vecteur
v[1..n] de entiers.
Exercice 2.5. On appelle bigramme une suite de deux lettres. Ecrire une fonction qui
calcule le nombre d’occurrences d’un bigramme donné dans un vecteur de caractères.
13
2.2.2. Algorithme d’accès à un élément d’un vecteur
Soit un vecteur v[1..n].
L’accès par position est défini par la primitive d’indiçage v[i].
L’accès associatif est le plus courant. Soit elem une variable ayant une valeur de même type
que celles contenues dans v, on veut déterminer s’il existe un indice i  [1..n] tel que v[i] = elem.
Cet accès peut être réalisé de plusieurs manières suivant que l’on suppose v trié ou non.

a) recherche séquentielle dans un vecteur non trié


On veut écrire une fonction dont l’en-tête sera :
booléen fonction accès1(d t v[], d entier n, d t elem)
spécification { n≥0 } => { résultat = elem  v[1..n] }

Algorithme : on doit parcourir le vecteur v[1..n], et à chaque fois on compare l’élément courant
v[i] à elem, si v[i] est différent de elem on prendra l’élément suivant. A priori, on écrirait
l’itération sous la forme :
tantque (i ≤ n) et (v[i] ≠ elem) faire …
Mais si elem є v[1..n], i atteint à la dernière itération la valeur n + 1, et on est amené alors à
effectuer un test sur v[n+1], valeur indéfinie.
On introduit donc l’opérateur "et alors" qui est en fait une évaluation de optimisée du "et "
classique. Il s’agit tout simplement de constater que si le premier terme du "et " est faux, il est
inutile d’examiner le second terme.
La table du "et alors" est la suivante.
14
Table du "et alors ".
A B A et alors B
vrai vrai vrai
vrai faux faux
faux non examiné faux
La valeur de C := A et alors B est équivalente à celle que donnerait la séquence
si A alors
C := B;
sinon
C := faux;
finsi;
On définit de même l’opérateur "ou sinon" , qui correspond à l’évaluation
optimisée du "ou" . Si le premier terme du "ou sinon" est vrai, il est inutile
d’examiner le second.
A B A ou sinon B
vrai non examiné vrai
faux vrai vrai
faux faux faux
15
La valeur de l’opération C := A ou sinon B est équivalente à celle que donnerait la
séquence
si A alors
C := vrai;
sinon
C := B;
finsi;

Remarques
La loi de De Morgan peut s’écrire, pour ces opérateurs :
non (a et alors b) = (non a) ou sinon (non b)
non (a ou sinon b) = (non a) et alors (non b)

De nombreux langages de programmation implémentent ces opérateurs :


soit directement (and then et or else de ADA, && et || de C)
soit sous forme d’options d’évaluation accélérée (certains compilateurs de Pascal).
S’ils n’existent pas, on est amené à simuler au moyen de valeurs booléennes.

16
L’algorithme s’écrit alors
booléen fonction accès1(d t v[], d entier n, d t elem)
debfonc
entier i;
i := 1;
tantque (i ≤ n) et alors (v[i] ≠ elem) faire
i := i + 1;
finfaire;
retourner i ≤ n ;
finfonc;
Etude du tableau de sortie
i>n V[i] = elem résultat
vrai non examiné faux
faux (i ≤ n) vrai vrai
faux (i ≤ n) faux impossible (tantque)

1ère ligne : i > n : on a dépassé la fin du vecteur, elem  v[1..n], le résultat est faux.
2ème ligne : i ≤ n, v[i] = elem : on a elem є v[1..n], le résultat est vrai.
3ème ligne : i ≤ n, v[i] ≠ elem : (i>n) ou v[i] = elem n’est pas une assertion.
Le résultat de la fonction accès1 est donc identique à celui de (i ≤ n). 17
On peut écrire cet algorithme sous forme récursive. Dans ce cas, il faut introduire la
variable i au niveau des paramètres, en tant que borne inférieure du vecteur que l’on
étudie.
Le raisonnement est alors :
 i>n résultat =faux *
 i≤n
►► v[i] = elem résultat = vrai *
►► v[i] ≠ elem nouvel appel (récursif) avec v[i+1..n]

booléen fonction accèsRec1 (d t v[], d entier i, d entier n; d t elem)


//spécification { n≥0 } => { résultat = elem  v[1..n] }
debfonc
si i > n alors retourner faux;
sinonsi v[i] = elem alors retourner vrai;
sinon retourner accèsrec1(v, i+1, n, elem);
finsi;
finfonc;

A l’appel de la fonction accèRec1, i peut avoir n’importe quelle valeur, en particulier


on peut lui donner la valeur 1 : b := accèsRec1(v, 1, n, elem).
18
Pour un parcours de gauche à droite, le raisonnement est le suivant :

 n=0 résultat =faux *


 n>0
►► v[n] = elem résultat = vrai *
►► v[n] ≠ elem nouvel appel (récursif) avec v[1..n-1]

Dans ce cas, on recherche elem dans v[1..n], et on n’a plus besoin de variable auxiliaire i,
puisque la borne inférieure du vecteur est toujours égale à 1.
booléen fonction accèsRec2 (d t v[], d entier n; d t elem)
spécification { n≥0 } => { résultat = elem  v[1..n] }
debfonc
si n = 0 alors retourner faux;
sinonsi v[n] = elem alors retourner vrai;
sinon retourner accèsrec1(v, n-1, elem);
finsi;
finfonc;
Si le vecteur n’est pas vide (n ≥ 1) un autre schéma d’algorithme consiste à prévoir la fin du
vecteur grâce à la variable i. Cette nouvelle version consiste à arrêter l’itération dès que i
atteint la valeur de n ou bien si on a trouvé l’élément cherché.
Avec le connecteur et alors, on pourrait éviter de vérifier dans le tantque si v[i] est égal à
elem lorsque i = n, mais on préfère présenter une version de l’algorithme qui évite l’emploi du
et alors même au prix d’un test supplémentaire lorsque v[n] = elem. 19
Algorithme : on doit parcourir le vecteur v[1..n], et à chaque fois on compare l’élément
courant v[i] à elem, si v[i] est différent de elem on prendra l’élément suivant. On arrête
l’itération dès que i atteint la valeur de n ou bien si on a trouvé l’élément cherché. A priori,
on écrirait l’itération sous la forme :
tantque (i < n) et (v[i] ≠ elem) faire …
Etude du tableau de sortie

i=n V[i] = elem résultat


vrai vrai vrai
vrai faux faux
faux (i < n) vrai vrai
faux (i < n) faux Impossible (tantque)

1ère ligne : v[i] = elem, i = n. On a v[n] = elem, donc elem appartient à v.


2ème ligne : v[i] ≠ elem, i = n. On a v[n] ≠ elem, donc elem n’appartient pas à v.
3ème ligne : v[i] = elem, i < n. On a v[i] = elem, i  [1..n-1]. On a trouvé un élément du
vecteur v égal à elem, donc elem appartient à v.
4ème ligne : v[i] ≠ elem, i ≠ n. (v[i] = elem) v (i = n) n’est pas une assertion.

Le résultat est donc identique à l’expression v[i] = elem.


L’algorithme s’écrit alors.
20
booléen fonction accès2(d t v[], d entier n, d t elem)
debfonc
entier i;
i := 1;
tantque (i < n) et alors (v[i] ≠ elem) faire
i := i + 1;
finfaire;
retourner v[i] = elem;
finfonc;

Il faut préférer cet algorithme à accès1 si l’on programme dans un langage qui ne
connait pas l’opérateur logique « et alors ». Il est toujours possible de simuler le
« et alors » au moyen de divers artifices, tels que l’emploi d’une variable booléenne
à la place du second test. Mais il est bien plus simple de s’en passer complètement, à
l’aide de l’algorithme accès ci-dessus.

On propose une dernière version de l’algorithme d’accès à un élément dans un


vecteur non trié, qui est particulièrement économique. Elle consiste à l’emploi d’une
« sentinelle » », que l’on ajoute à la fin du vecteur.
Il suffit d’insérer la valeur recherchée elem dans un emplacement à la fin du vecteur,
pour ne pas avoir à tester chaque fois la valeur de i. 21
1 n n+1
- - - ---- -- elem

booléen fonction accès3(d t v[], d entier n; d t elem)


debfonc
entier i;

i := 1;
tantque v[i] ≠ elem faire
i := i + 1;
finfaire;
retourner i ≤ n;
finfonc;

Exercice 2.6.
Ecrire une fonction qui calcule le produit des éléments d’un vecteur v[1..n] d’entiers (penser
au cas où un élément serait nul).

Exercice 2.7.
Ecrire de plusieurs manières différentes une fonction entière qui délivre l’indice de la valeur
val dans v[1..n] si val appartient au vecteur v et 0 si val n’appartient pas à v. Préciser, dans
chaque cas, si c’est la première occurrence ou la dernière occurrence que l’on a trouvée.
22
b) vecteur ordonné (ou trié)
Si tous les éléments consécutifs d’un vecteur vérifient la relation d’ordre v[i-1]v[i], on dit que le
vecteur est trié par ordre croissant.
Dans toute la suite du cours, on appellera vecteur trié (ou ordonné) un vecteur trié par ordre
croissant.
Définition.
• un vecteur vide (n=0) est ordonné,
• un vecteur contenant un seul élément (n=1) est ordonné,
• un vecteur v[1..n], n>1, est ordonné si i  [2..n], v[i-1]  v[i].
On peut également donner une définition récursive.
• un vecteur vide (n=0) est ordonné,
• un vecteur contenant un seul élément (n=1) est ordonné,
• (v[1..i-1]ordonné, v[i-1]  v[i])  v[1..i] ordonné pour i  [2..n].
Cette définition récursive est à rapprocher des définitions récursives utilisées en mathématiques,
telles que la définition de n! :
n ! = 1 si n = 0,
n ! = n * (n - 1) ! si n  1.
Ces définitions sont utilisées pour construire des « algorithmes récursifs ».
Application immédiate de la définition est l’algorithme vérifiant qu’un vecteur est trié.
On veut écrire un algorithme d’en-tête :
fonction trié(d t v[], d entier n) : booléen
/spécification {n0}  {résultat = v[1..n] est trié}
Il suffit de comparer tous les couples de deux éléments consécutifs en s’arrêtant dès qu’un couple
ne vérifie pas la relation d’ordre v[i-1]v[i]. 23
Cet algorithme est très semblable à celui de l’accès séquentiel accè1, mais que la valeur
retournée est vrai si et seulement si on a bien atteint i >n.
Notons que les cas n = 0 et n = 1 sont correctement traités puisque la valeur initiale de i
est supérieure à n dans ces cas.
booléen fonction trié(d t v[], d entier n)
debfonc
entier i;
i := 2;
tantque (i ≤ n) et alors (v[i] ≤ elem) faire
i := i + 1;
finfaire;
retourner i > n ;
finfonc;
Etude du tableau de sortie
i>n v[i - 1] > v [i] résultat
vrai non examiné vrai
faux (i ≤ n) vrai faux
faux (i ≤ n) faux impossible (tantque)

Le résultat n’est vrai que si i > n. 24


Accès à un élément dans un vecteur trié
On veut écrire une fonction d’en-tête
booléen fonction accèsTrié1 (d t v[], d entier n, d t elem)
spécification { n≥0, v[1..n] trié } => { résultat = elem  v[1..n] }
On cherche donc un indice i  [1..n] tel que v[1..i-1] < elem ≤ v[i..n], soit une
assertion.
v[1..i-1] < elem elem ≤ v[i..n]

1 i n
Ensuite, il ne reste plus qu’à vérifier l’égalité elem = v[i] pour savoir si elem est
présent ou non dans le vecteur trié.
booléen fonction trié(d t v[], d entier n, d t elem)
debfonc
entier i;
i := 1;
tantque (i ≤ n) et alors (v[i] < elem) faire
i := i + 1;
finfaire;
retourner (i ≤ n) et alors (v[i] = elem) ;
finfonc; 25
Etude du tableau de sortie
i>n v[i]  elem résultat
vrai non examiné faux
faux (i  n) vrai vrai ssi elem = v[i]
faux (i  n) faux impossible (tantque)

1ère ligne : i>n, on a dépassé la fin du vecteur, donc elem  v[1..n]. Le résultat est faux.
2ème ligne : i  n, v[i]  elem, si v[i]=elem alors elem appartient à v et le résultat prend
la valeur vrai, sinon il prend la valeur faux.
3ème ligne : i  n, v[i] < elem, (i < n) ou sinon (v[i]  elem) n’est pas une assertion.

Le résultat de la fonction accèstrie1 est identique à la valeur de :


(i  n) et alors (v[i]= elem).
On aurait aussi bien pu écrire :
si i  n alors
retourner v[i] = elem;
sinon
retourner faux;
Si le vecteur n’est pas vide, on peut s’ s’arrêter sur l’avant-dernier élément, afin
d’éviter l’emploi du « et alors ». 26
booléen fonction accèsTrié2(d t v[], d entier n, d t elem)
//spécification {n>0, v[1..n]trié} {résultat = elem  v[1..n] }
debfonc
entier i;
i := 1 ;
tantque (i < n) et (v[i]< elem) faire i++;
retourner v[i] = elem;
finfonc;

Tableau de sortie
i=n V[i] ≥ elem résultat
vrai vrai vrai ssi elem = v[i]
vrai faux faux
faux (i < n) vrai vrai ssi elem = v[i]
faux (i < n) faux Impossible (tantque)

Le résultat est bien identique à la valeur de v[i] = elem.

27
Si le vecteur n’est pas vide, on peut aussi comparer l’élément que l’on cherche avec
celui du dernier élément de v[1..n]. Alors, si elem > v[n], l’algorithme est terminé,
l’élément n’appartient pas à v. Si elem  v[n], on peut effectuer un parcours de
gauche à droite avec une seule condition (v[i]<elem) au niveau du tantque car on
est certain que i est borné par n (puisque v[n]  elem).
L’algorithme est alors :
booléen fonction accesTrié3(d t v[], d entier n, d t elem)
//spécification {n > 0, v[1..n] trié}{résultat = elem v[1..n]}
debfonc
entier i;
si elem > v[n] alors retour faux ;
sinon
i := 1 ;
tantque v[i] < elem faire
i++;
finfaire;
retourner v[i] = elem ;
finsi;
finfonc;
28
Une dernière version possible consisterait à utiliser une "sentinelle« , qui serait cette fois non
pas la valeur ci, mais un majorant (valeur supérieure dans v[1..n], high value).
Exercice 2.8.
Ecrire l’algorithme de recherche d’une valeur dans un vecteur trié, à l’aide d’une sentinelle.
Exercice 2.9.
Ecrire une fonction qui retourne la place de la première occurrence de la valeur val dans le
vecteur trié v[1..n] si val appartient au vecteur v, et 0 si val n’appartient pas à v.
Exercice 2.10.
Même question pour la dernière occurrence.

Evaluation du coût d’algorithme séquentielle dans un vecteur


On calculera le nombre d’éléments indicé qui sont toujours très coûteux par rapport aux
accès à un élément non indicé. On notera qu’une comparaison entre deux éléments indicés
demande deux accès (v[i], v[j]) alors qu’une comparaison entre un élément indicé et un
élément non indicé ne demande qu’un accès indicé.
On distingue deux cas :
• Le vecteur v[1..n] n’est pas trié, il faut :
** n comparaisons (n accès) si elem  v
** n/2 comparaisons (n/2 accès) en moyenne si elem  v.
• Le vecteur v[1..n] est trié, il faut en moyenne n/2 comparaisons (n/2 accès) que l’élément
appartienne ou non au vecteur v.

29
c) Recherche dichotomique

On cherche à écrire une fonction d’en-tête :


booléen fonction dich(d t v[], entier n, d t elem)
spécification {v trié} {résultat = elem  v[1..n]}

Supposons le vecteur v[1..n]


 (n≥1), ordonné :
i  [2..n], v[i-1] ≤ V[i]

On va partitionner v en trois sous-vecteurs v[1..m-1], v[m..m], v[m+1..n] ordonnés.


Cette partition vérifie la relation : v[1..m-1]  v[m]  v[m+1..n].

En comparant elem à v[m], on peut décider, si elem n’est pas égal à v[m], à quel sous-
vecteur il peut appartenir :
• à v[1 .. m-1] si elem < v[m],
• à v[m+1 .. n] si elem > v[m].

On est alors ramené au même problème que précédemment : rechercher elem dans un
vecteur (v[1..m-1] ou v[m+1..n]). Par contre si elem est égal à v[m], l’algorithme est
terminé. 30
On est donc amené à déterminer une suite de sous-vecteurs : v1, v2,…, vk telle que
chaque sous-vecteur vi a un nombre d’éléments strictement inférieur à la taille du
vecteur précédent vi-1.
Remarque
On choisit l’indice m tel que la taille des deux sous-vecteurs v[1..m-1] et v[m+1..n] soit
la même (à un élément près). La taille des vecteurs de la suite v1, v2,…, vk est divisée
par deux à chaque pas : n, n/2, …,n/2k-1. On a donc au plus « partie entière de log2(n)»
sous-vecteurs non vides.
booléen fonction dich(d t v[], entier n, d entier elem)
debfonc
entier inf, sup, n; booléen trouvé;
trouvé := faux ; inf := 1 ; sup := n ;
tantque (infsup) et (non trouvé) faire
m := (inf + sup) div 2 ;
si v[m] = elem alors trouvé := vrai ;
sinonsi v[m] < elem alors inf := m + 1 ;
sinon sup := m – 1 ;
finsi;
finfaire;
retourner trouvé ;
finfonc; 31
Etude de tableau de sortie
inf > sup trouvé résultat : dich
vrai vrai impossible
vrai faux faux
faux vrai vrai
faux faux Impossible (tantque)

1ère ligne : inf > sup, trouvé


Impossible à cause de si-sinon : trouvé prend la valeur vrai que si inf ≤ sup.
2ème ligne : inf > sup, ¬ trouvé
On a inf = sup+1 et ¬ trouvé qui conduit à (¬ trouvé, elem  v).
3ème ligne : inf ≤ sup, trouvé
On a trouvé l’élément elem dans le vecteur v : (trouvé, elem  v)
4ème ligne : inf ≤ sup, ¬ trouvé
impossible : (inf > sup) v (trouvé) n’est pas une assertion.

La valeur de dich est identique à celle de trouvé.


Pour accélérer l’exécution de cet algorithme, on ajoute un test initial pour savoir si
elem est bien compris entre les deux éléments extrêmes du vecteur. 32
booléen fonction dich(d t v[], d entier n, d t elem)
defonc
entier inf, sup, n; booléen trouvé;
trouvé := faux ;
si (elem ≥ v[1]) et (elem ≤ v[n]) alors
inf := 1 ; sup := n ;
tantque (infsup) et (non trouvé) faire
m := (inf + sup) div 2 ;
si v[m] = elem alors trouvé := vrai ;
sinonsi v[m] < elem alors inf := m + 1 ;
sinon sup := m – 1 ;
finsi;
finfaire;
finsi;
retourner trouvé ;
finfonc;
Sous-forme récursive, on cherche elem dans v[inf..sup], le raisonnement est :
 inf > sup, v[inf..sup] est vide, elem  v[inf..sup] résultat = faux *
 inf ≤ sup, m := (inf + sup) div 2;
>> v[m] = elem, elem  v[inf..sup] résultat = vrai *
>> v[m] < elem, on recommence la recherche dans v[m+1..sup]
>> v[m] > elem on recommence la recherche dans v[inf..m-1]
33
Sous-forme récursive, l’algorithme est :
booléen fonction dich(d t v[], d entier inf, d entier sup, d t elem)
spécification {v trié}  {résultat=elem v[inf..sup]}
debfonc
entier m;
si inf > sup alors retourner faux ;
sinon
m := (inf +sup) div 2 ;
si v[m] = elem retourner vrai ;
sinonsi v[m]<elem retour dich(v, m+1,sup, elem) ;
sinon retourner dich(v, inf, m–1, elem) ;
finsi;
finsi;
finfonc;
Note : on peut appeler cette fonction avec n’importe quelle valeur de inf et de sup :
c’est-à-dire avec n’importe quel sous-vecteur v[inf..sup].

Exercice 2.11. Modifier les fonctions dich données ci-dessus afin qu’elles délivrent la
place de elem dans le vecteur v s’il y figure, et 0 s’il n’y figure pas (on supposera qu’
elem ne figure pas plus d’une fois dans v, on dira que le vecteur est trié sans répétition).
34
2.3. Tris d’un vecteur
Soit v[1..n] , trier le vecteur v consiste à construire un vecteur v’[1..n] tel que :
• v’ soit trié,
• v et v’ contiennent les mêmes éléments.

2.3.1. Tri par remplacement


Cette méthode est simple, mais a des performances très faibles.
Elle consiste à sélectionner des minimums successifs dans le vecteur d’origine, et à les
ranger au fur et à mesure dans le vecteur à construire.

Méthode
Construire vtrié[1..n] à partir de v[1..n] tel que vtrié[i-1]  vtrié[i], i  [2..n].
Pour i = 2, on a vtrié[1]  vtrié[2..n]. vtrié[1] est donc égal à : minimum(v[1..n]).

Il faut maintenant substituer dans v à la valeur du minimum trouvé, une valeur qui ne
sera plus atteinte par la suite, lors de la recherche d’un nouveau minimum. Il suffit de
prendre comme valeur de remplacement un majorant de v[1..n] : le maximum des
éléments de v[1..n].

Exemple : soit à trier les lettres du mot BATEAUX : le maximum est égal à ‘X’.
35
V j (indice du minimum) V après remplacement Vtrié
1 B/ATEAUX 2 BXTEAUX A
2 BXTEAUX 5 BXTEXUX AA
3 BXTEXUX 1 XXTEXUX AAB
4 XXTEXUX 4 XXTXXUX AABE
5 XXTXXUX 3 XXXXXUX AABET
6 XXXXXUX 6 XXXXXXX AABETU
7 XXXXXXX fini AABETUX

Il faut écrire un algorithme de tri qui utilise deux fonctions :

• une fonction qui retourne la place de l’élément minimum du vecteur v.


entier fonction indmin(d t v[], d entier n)
spécification { n>0 } { résultat = j, j  [1..n], v[j]  v[1..n] }

• une fonction qui retourne la valeur du plus grand élément du vecteur v.


t fonction maximum(d t v[] , d entier n)
spécification {n>0} { résultat = max, max  v[1..n], max  v[1..n] }
36
Fonction indmin
entier fonction indmin(d t v[], d entier n)
debfonc
entier i, j;
j := 1 ; i := 2 ;
tantque i  n faire
si v[j] > v[i] alors j := i ;
finsi;
i := i +1 ;
finfaire;
retourner j ;
finfonc;
Fonction maximum
t fonction maximum(d t v[], d entier n)
debfonc
entier i; t max;
max := v[1]; i := 2;
tantque i  n faire
si v[i] > max alors max := v[i] ;
finsi;
i := i +1;
finfaire;
retourner max;
37
finfonc;
Algorithme de tri
procédure triRemplacement(dr t v[], r t vtrie[], d entier n)
//spécification {n>0}{vtrie[1..n], vecteur contenant tous les éléments de v[1..n] mais triés
par ordre croissant }
debproc
t max; entier i, j;
max := maximum(v, n); i := 1;
tantque i < n faire
j := indmin(v, n) ; vtrie[i] := v[j];
v[j] := max ; i := i +1 ;
finfaire;
vtrie[n] := max;
finproc;

Le type t est le type des éléments du vecteur V. On notera que l’on a été amené à modifier le
vecteur initial V, c’est pourquoi il est passé en paramètre « donnée-résultat ». Si on voulait
conserver sa valeur initiale, il faudrait le recopier dans un autre vecteur avant d’appeler cette
procédure.

Exercice 2.12.
Modifier triRemplacement pour qu’il trie les éléments de V dans l’ordre décroissant.

38
Evaluation du coût de l’algorithme

Soit n la taille du vecteur à trier. On se propose de dénombrer les opérations portant


sur les éléments du vecteur.

Coût de la fonction maximum


• Nombre de comparaisons
On effectue autant de comparaisons (v[i]>max) que d’itérations. D’où n-1
comparaisons soit n-1 accès.
• Nombre d’affectations
A l’initialisation, on a une affectation. Ensuite, suivant l’ordre des éléments de V, on
effectue ou non une affectation à chaque itération. Dans le cas favorable, on effectue
aucune affectation (V trié en ordre décroissant), alors que dans le cas défavorable, on
a effectue n-1 (V trié en ordre croissant). En conclusion, on obtient :
Cas favorable : 1 affectation (1 accès),
Cas défavorable : n affectations (n accès).

Coût de la fonction indmin


Nombre de comparaisons : on effectue une comparaison entre deux éléments de
vecteur à chaque itération. On a donc n-1 comparaisons (2(n-1) accès).
39
Coût de la procédure triRemplacement
• Place occupée : l’algorithme utilise deux vecteurs V et Vtrié. Si t est la place occupée
par un élément de vecteur, la place totale occupée est 2nt.

• Nombre d’affectations : pour chaque itération on a :


** 1 affectation pour substituer la valeur de l’élément maximum (1 accès).
** 1 affectation pour construire le vecteur Vtrié (2 accès).
On effectue n-1 itérations ce qui donne 2(n-1) affectations (3(n-1) accès) plus une
affectation (1 accès) pour le dernier élément d’où 2n-1 affectations (3n-2 accès). La
fonction maximum est appelée une fois. Il faut donc ajouter, suivant le cas, 1
affectation (1 accès) ou n affectations (n accès). En définitive, on obtient :
2n affectations (≈3n accès) dans le cas favorable,
3n-1 affectations dans le cas défavorable.

• Nombre de comparaisons :
On appelle la fonction maximum une fois d’où : n-1 comparaisons ou (n-1 accès).
D’autre part, à chaque itération, on appelle la fonction indmin qui demande n-1
comparaisons (2(n-1) accès). Comme on effectue n-1 itérations, on effectue (n-1)(n-1)
comparaisons pour l’appel de la fonction indmin (≈2n2 accès).
On obtient donc : n(n-1) comparaisons (≈ 2n2 accès).
40
En conclusion
Place occupée 2nt
Nombre de comparaisons n(n-1) (≈2n2 accès)
Nombre d’affectations Cas favorable 2n (3n accès)
Cas défavorable ≈3n (≈4n accès)
Exemple
Si n=1000, t=20 octets.
Place occupée : 40 000 octets.
Nombre de comparaisons : de l’ordre de 1 000 000.
Nombre d’affectations : de l’ordre de 2 500 en moyenne.

2.3.2. Algorithmes de tri par permutation


Afin d’éviter la construction d’un 2ième vecteur on confond v et vtrié. Ce qui améliore
le coût dû à l’encombrement mémoire. Aussi le nombre d’accès au vecteur est diminué.

Méthode
Supposons traités i – 1 (1  i < n) éléments du vecteur v. On a la configuration suivante:
v[1..i-1]trié et v[i..n] non trié.
41
V[1..i-1] trié V[i..n] non traité

1 i-1 i n
Le vecteur v est la concaténation de 2 vecteurs : v[1..i-1] triés et v[i..n] non traités.
D’autre part tous les éléments du sous-vecteur v[1..i-1] sont inférieurs ou égaux à tous
les éléments du sous-vecteurs v[i..n].
Algorithme : à chaque parcours du vecteur v[i..n], on cherche l’indice du minimum (j),
si j≠i , on permute les valeurs v[i] et v[j] sinon on ne fait rien, puis à chaque cas, on
prend l’élément suivant.
Exemple : soit à trier dans l’ordre alphabétique les lettres du mot BATEAUX.
i j trié/ non traité éléments à permuter Après permutation, trié/non
traité
1 2 /BATEAUX B et A A/BTEAUX
2 5 A/BTEAUX B et A AA/TEBUX
3 5 AA/TEBUX T et B AAB/ETUX
4 4 AAB/ETUX pas de permutation AABE/TUX
5 5 AABE/TUX pas de permutation AABET/UX
6 6 AABET/UX pas de permutation AABETU/X
7 AABETU/X fini AABETUX/ 42
Algorithmes : supposons que nous disposions de 2 primitives.
• entier fonction indmin(d t v[], d entier i, d entier n)
spécification {1i<n}  {résultat = j, j[i..n], v[j]  v[i..n]}
• procédure permut(dr entier a, b)
spécification {a = x, b = y} {a = y, b = x }

Algorithme de tri par permutation

procédure tripermutation(dr t v[], d entier n)


//spécification {} {v[1..n] trié}
debproc
entier i, j;
i := 1;
tantque i < n faire
j := indmin(v, i, n) ;
si ji permut(v[i], v[j]);
finsi;
i := i + 1 ;
finfaire;
finproc;
43
fonction indmin c’est le même algorithme que le précédent. La seule différence est
l’indice de départ qui est un paramètre i au lieu de la constante 1.
entier fonction indmin(d t v[], d entier i, d entier n)
spécification {1i<n}  {résultat = j, j  [i..n], v[j]  v[i..n] }
debfonc
entier k, j;
j := i ; k := i + 1 ;
tantque k n faire
si v[j] > v[k] faire j := k;
finsi;
k := k +1;
finfaire;
retourner j;
finfonc;

Exercice 2.13
Modifier le tri par permutation pour obtenir un vecteur trié dans l’ordre décroissant.

Evaluation du coût de l’algorithme


Coût de la procédure permut
• Nombre d’affectation : 3 (soit 4 accès indicés) 44
• Place occupée
Une place, de la taille t d’un élément du vecteur, est occupée par la variable
auxiliaire utilisée par la permutation, on a un seul vecteur, donc : (n+1)t.
Coût de la fonction indmin
Il suffit de dénombrer les comparaisons effectuées entre deux éléments du vecteur.
On notera qu’on effectue une comparaison à chaque itération. On a donc (m-1)
comparaisons si m est la taille du vecteur, soit 2(m-1) accès.
Coût de la procédure tripermutation
• Nombre de comparaisons
A chaque itération, on appelle la fonction indmin (V[i..n]). Or i varie de 1 à n-1 d’où
n-1 comparaisons pour le 1er appel,
n-2 comparaisons pour le 2ème appel,

n-j comparaisons pour le jème appel,
….
1 comparaison pour le dernier appel.
Le nombre total de comparaisons est égal à : n-1
nc = n-1 + n-2 + …+ n-j +…+ 2 + 1 = ∑ j = n(n-1)/2
• Nombre de permutations 1
Dans le cas favorable (le vecteur est déjà trié), on n’appelle jamais la procédure de
permutation. 45
Dans le cas défavorable, on n’appelle jamais la procédure de permutation à chaque
itération. On obtient donc (n-1) permutations.
En conclusion

Place occupée (n+1)t


Nombre de comparaisons n(n-1)/2 (≈ n2 accès)
Nombre de permutations cas favorable n
cas défavorable n-1 (≈ 4n accès)

Exemple
Pou n = 1000, t = 20 octets,
Place occupée : 20020 octets
Nombre de comparaisons : 500 000,
Nombre d’affectations : de l’ordre de 1500 en moyenne.

On constate que cet algorithme est bien meilleur que le précédent car il a permis de
gagner de la place et du temps, ce qui est très rare en informatique. Le plus souvent,
un gain de temps a pour contrepartie une perte de place et un gain de place a pour
contrepartie une perte de place. Cela signifie que l’algorithme triRemplacement est
très mauvais et qu’il ne faut jamais l’utiliser. Il a été donné uniquement à titre
pédagogique. 46
2.3.3. Tri par la méthode des bulles
Il s’agit d’une méthode qui opère par permutations à l’intérieur du vecteur à trier. La
première version (non optimisée) n’est qu’une variante , moins efficace de tri par
permutation. la seconde version (optimisée) est généralement un peu meilleure.

Principe
Après avoir traité i-1 (1  i < n) éléments, on a la situation suivante :
v[1..i-1]trié v[i..n] non trié

1 i-1 i n

On retrouve la situation vue dans le tri par permutation.


Pour augmenter le sous/vecteur v[1..i-1] d’un élément, il faut chercher le plus petit
élément dans le sous/vecteur v[i..n] et placer cet élément en position i.
Nous allons cette fois, au lieu de la recherche du minimum suivi d’une permutation,
utiliser la méthode dite des bulles. On parcourt le sous/vecteur v[i..n] de droite à
gauche et, chaque fois qu’il y a 2 éléments consécutifs qui ne sont pas dans l’ordre, on
les permute. Cette opération permet d’obtenir en fin de parcours le plus petit élément de
v[i..n] en position i.
47
En effet, supposons traités les n – j +1 derniers éléments du sous/vecteur v[i..n], on a la
configuration suivante :
v[i..j-1] non traité v[j..n] traité

i j-1 j n
avec v[j]  v[j..n].
Exemple : soit à ranger en position 1 l’élément minimum du vecteur BATEAUX.
J non traité/ traité éléments à comparer Après permutation non traité/ traité
7 BATEAU/X U et X pas de permutation BATEA/UX
6 BATEA/UX A et U pas de permutation BATE/AUX
5 BATE/AUX E et A permutation BAT/AEUX
4 BAT/AEUX T et A permutation BA/ATEUX
3 BA/ATEUX A et A pas de permutation B/AATEUX
2 B/AATEUX B et A permutation /ABATEUX

On constate que, non seulement l’élément minimum A a bien été rangé dans la position
voulue, mais que l’ordre général s’est accru : le second A a été aussi déplacé vers la
gauche. C’est cette opération qui donne son nom à la méthode : on fait remonter les
48
bulles vers la surface : les éléments les plus légers sont transportés vers la gauche.
L’algorithme de remontée s’écrit donc.
j := n;
tantque j > i faire
si v[j -1] > v[j] alors permut(v[j-1], v[j]);
finsi;
j := j – 1 ;
finfaire;

Ou encore : pour j := n bas i +1 faire


si v[j-1] > v[j] permut(v[j], v[j-1]);
finsi;
finfaire;

L’algorithme du tri complet


Pendant le parcours du vecteur de i à n, on fait des permutations (« bulles») de telle
sorte que le minimum de v[i..n] soit placé en v[i], puis on prend l’élément suivant.
Quand i atteint n (i=n), on s’arrête.

49
Soit à trier dans l’ordre alphabétique les lettres du mot « BATEAUX ».

i trié/ non traité éléments minimum de la après remontée des bulles


partie non traitée trié/non traité
1 /BATEAU/X A A/BATEUX
2 A/BATEUX A AA/BETUX
3 AA/BETUX B AAB/ETUX
4 AAB/ETUX E AABE/TUX
5 AABE/TUX T AABET/UX
6 AABET/UX U AABETU/X
7 AABETU/X le vecteur est trié

On peut maintenant écrire une première version de l’algorithme complet du tri par la
méthode des bulles.

50
procédure tribulle1(dr t v[], d entier n)
debproc
entier i, j;
i := 1;
tantque i < n faire
j := n ;
tantque j > i faire
si v[j -1] > v[j] permut(v[j-1], v[j]);
finsi;
j := j – 1;
finfaire;
i := i + 1;
finfaire;
finproc;
Version avec des instructions pour.
procédure tribulle1(dr t v[], d entier n)
debproc
entier i, j;
pour i := 1 haut < n-1 faire
pour j := n bas i+1 faire
si v[j -1] > v[j] alors permut(v[j-1], v[j]) ;
finsi;
finfaire;
finfaire; 51
finproc;
Evaluation du coût de l’algorithme
Les comparaisons entre les éléments du vecteur sont effectuées dans l’itération «tantque j>i ».
On effectue une comparaison à chaque itération d’où : n-i comparaisons, soit 2(n-i) accès.
l’itération « tantque j>i » de trouve placée dans l’itération « tantque i<n » .
Pour i=1, on effectue n-1 comparaisons,
Pour i=2, on effectue n-2 comparaisons,

Pour i=n-1, on effectue 1 comparaison,
Le nombre total de comparaisons nc est égal à : nc = n(n-1)/2 d’où n(n-1) accès.
Pour le nombre de permutations, on a deux cas :
• le cas favorable où on effectue o permutations,
• le cas défavorable où on effectue une permutation à chaque comparaison :
n(n-1)/2 permutations, donc 2n(n-1) accès.
La place occupée est le même que dans l’algorithme de tri précédent.
Les performances sont identiques sauf en ce qui concerne le nombre de permutations qui peut
être beaucoup plus grand.
En conclusion :

Place occupée (n+1)t


Nombre de comparaisons n(n-1)/2 (≈n2 accès)
Nombre de permutations cas favorable 0
cas défavorable n(n-1)/2 (≈2n2 accès)
52
Optimisation de l’algorithme
Après avoir traité i–1 (1i<n) éléments, on a v[1..i-1]trié, v[1..i-1]v[i..n], v[i..n] non
traité.
Si v[i..n] est trié à la suite des permutations effectuées, il est inutile de continuer car
(v[1..i-1]trié, v[1 ..i-1]v[i], v[i..n]trié)  v[1..n] trié.
v[i..n] est trié si aucune permutation n’a été effectuée lors du parcours du s/vec v[i..n].
Nous utilisons une variable booléenne onapermute dont la valeur est vrai si l’on a
effectué une permutation, faux si aucune permutation n’a été effectuée lors du parcours
du s/vec v[i..n]. On initialise cette variable à faux avant chaque parcours de v[i..n].
Par contre, à chaque parcours du s/vec v[i..n], chaque fois que l’on effectue une
permutation, on donne à la variable onapermute la valeur vrai.
D’autre part, tantque(i<n) devient tantque((i<n) et onapermute) afin d’ arrêter le
parcours dès que onapermute reste à la valeur faux après avoir effectué le parcours de
v[ i..n].
Pour savoir si le test (i<n) est encore nécessaire. Examinons ce qui se passerait si on le
supprimait. Dans le cas le plus défavorable et à chaque parcours de v[i..n] on a effectué
au moins une permutation. Pour la valeur i = n, onapermute prend la valeur faux et on
ne peut pas effectuer de permutation puisqu’il ne reste qu’un seul élément ;
onapermute reste à la valeur faux, ce qui permet d’arrêter l’algorithme.
En conclusion : l’itération tantque(onapermute) est suffisante. Le s/vec v[i..n] est trié
si, après avoir parcouru le s/vec v[i..n], la variable onapermute a la valeur faux. 53
procédure tribulle2(dr t v[], d entier n)
debproc
entier i, j; booléen onapermute;
i := 1; onapermute := vrai;
tantque onapermute faire
j := n; onapermute := faux;
tantque j > i faire faire
si v[j -1] > v[j]
permut(v[j-1], v[j]);
onapermute := vrai;
finsi;
j := j – 1;
finfaire;
i := i + 1;
finfaire;
finproc;

Cette fois il est impossible d’écrire un algorithme avec deux boucles pour.

54
procédure tribulle2( dr t v[], d entier n)
spécification(n>0)(v[1..n] trié)
debproc
entier i, j; booléen onapermute;
i := 1; onapermute := vrai;
tantque onapermute faire
onapermute := faux;
pour j := n bas i+1 faire
si v[j -1] > v[j] alors
permut(v[j-1], v[j]);
onapermute := vrai;
finsi;
finfaire;
i := i + 1;
finfaire;
finproc;

55
Exercice 2.14 : Modifier les procédures triBulle1 et triBulle2 pour que V soit trié dans
l’ordre décroissant.

Exercice 2.15 : Soit un vecteur de personnes identifiés par un nom et un prénom.


Ecrire une procédure de tri de V dans l’ordre croissant des identités. On pourra
utiliser les déclarations suivantes :
type personne = structure
nom, prénom : chaine25;
informations : …;
fin;
nbpers = 100;
vectpers = tableau[nbpers] de personne;

56
2.4. Algorithmes de mise à jour d’un vecteur

Nous nous limitons à des algorithmes d’insertion et de suppression d’un seul élément
dans un vecteur.

2.4.1. Insertion d’un élément dans un vecteur non trié


S’il n’y a aucun critère d’insertion, il suffit d’ajouter le nouvel élément à la fin du
vecteur et de ne pas oublier de mettre à jour le nombre d’éléments du vecteur.

procédure insertionfin(dr t v[], d entier n, d t elem)


/* spécification (n≥0)(ajout d’un élément à la fin de v) */
{ n := n + 1;
v[n] := elem;
}

Si au contraire, on veut ajouter un élément à la kième place, ou après une occurrence


d’une valeur, alors les algorithmes sont du même type que celui de l’insertion d’une
valeur dans un vecteur trié, présenté ci-après.

57
2.4.2. Insertion d’un élément dans un vecteur trié
Il s’agit d’ajouter au vecteur trié vtrie[1..n], l’élément elem pour obtenir un nouveau
vecteur trié vtrié[1..n+1].
Une méthode consiste à :
• chercher la place de l’élément à insérer,
• effectuer l’insertion de cet élément.

L’algorithme d’insertion
procédure insertion(dr t vtrie[], d entier n, d entier elem)
debproc
entier p;
si n = 0 alors
vtrie[1] := elem ; n := 1;
sinon /* n > 0 */
p := posit(vtrie, n, elem);
insertplace(vtrie, n, elem, p) ;
finsi;
finproc;
Où posit est une fonction délivrant la place de l’élément elem.
insertplace une fonction d’insertion de elem à la place p. 58
Recherche de la place de l’élément à insérer
On cherche l’indice p (p  [1..n+1]) tel que la relation suivante soit vérifiée
vtrie[1..p-1]  elem < vtrie[p..n]
En effet, on cherche une place le plus à droite possible, afin d’avoir à effectuer le
moins possible de décalages pour l’insertion.
Exemple. Si le vecteur v contient les lettres : A B B C F F G, on devra trouver :
posit(v, 6, ‘A’) = 1
posit(v, 6, ‘B’) = 3
posit(v, 6, ‘C’) = 4
posit(v, 6, ‘F’) = 6
posit(v, 6, ‘G’) = 7
On remarquera que pour p = 1, la relation se réduit à elem < vtrie[1..n]
et que pour p = n + 1 elle se réduit à vtrie[1..n]  elem.
Cette recherche peut être effectuée par une méthode séquentielle ou dichotomique.
Méthode séquentielle
On commence par chercher si p = 1 ; sinon il reste à chercher p dans l’intervalle
[2 ..n+1]. Pour cela, on effectue un parcours de droite à gauche .
Hypothèse : vtrie[1]  elem < vtrie[i+1..n]
• vtrie[i]  elem  p = i+1 ; *
• vtrie[i] > elem  i = i-1 ;  H
Itération : tantque(vtrie[i] > elem)… 59
Initialisation
• vtrie[1] > elem  p = 1; *
• vtrie[1]  elem  i = n ; H

L’algorithme
entier fonction posit(d t v[], d entier n, d t elem)
//spécif (n>0, vtrie[1..n] trié) (résultat=p, vtrie[1..p-1]elem<vtrie[p..n], p[1..n+1])
debfonc
entier p, i;
si vtrie[1]>elem alors p:=1;
sinon
i := n;
tantque(vtrie[i] > elem)i:=i-1;
p := i+1;
finfaire;
finsi;
retourner p;
finfonc;
Méthode dichotomique
On traite d’abord le cas de p=n+1. Ensuite il reste à chercher p[1..n]
tel que vtrie[1..p-1]  elem < vtrie[p..n]. 60
entier fonction posit(d t vtrie[], d entier n, d t elem)
debfonc
entier p, inf, sup, m;

si vtrie[n]elem alors p := n + 1;
sinon
inf := 1 ; sup := n ;
tantque inf < sup faire
m := (inf + sup)/2;
si vtrie[m]elem alors inf := m + 1;
sinon sup := m;
finsi;
finfaire;
p := sup;
finsi;
retourner p;
finfonc;

61
Insertion de l’élément à sa place
Connaîssant la place p de l’élément à insérer, le nouveau vecteur vtrie[1..n+1] est égal
à la concaténation de 3 vecteurs vtrie[1..p-1] || elem || vtrie[p+1..n+1].
où vtrie[p+1..n+1] est identique à vtrie[p..n].
Il suffit d’effectuer une translation à droite d’une position des éléments de vtrie[p..n] et
ensuite d’affecter elem à vtrie[p].
procédure insertplace(dr t vtrié[], d entier n, d t elem, d entier p)
debproc
pour i := n bas p faire vtrié[i+1] := vtrié[i];
finfaire;
n++;
vtrie[p] := elem;
finproc;

2.4.3. Suppression d’un élément dans un vecteur trié


Toutefois, la valeur à supprimer n’appartient pas obligatoirement au vecteur v[1..n],
dans ce cas la suppression est impossible. On définira une variable booléenne possible
permettant de savoir si la suppression a été possible ou non. Nous nous intéressons à la
suppression d’un élément dans un vecteur trié.
62
L’algorithme peut se décomposer en 2 parties :
• rechercher une occurrence de la valeur elem à supprimer,
• si on a trouvé une occurrence, retasser les éléments du vecteur afin d’obtenir un
vecteur qui ne contienne plus que n-1 éléments.
Convention pour la recherche d’1 occurrence : on cherche l’indice p le plus grand
possible tel p[1..n], vtrie[p]=elem. Si la valeur elem est inexistante, on donne par
convention à la variable p la valeur zéro.
Algorithme
procédure supp(dr t vtrié[], d entier n, d t elem, r booléen possible)
debproc
entier p;
possible := faux;
si p > 0 alors
tasser(vtrié[1..n], p) ;
possible := vrai ;
finsi;
finproc;
Recherche de la place de l’élément
On cherche un indice tel que : d’une part p [1..n] et d’autre part elem  vtrie[1..n], p
= 1 ou bien vtrie[p]=elem, elem<vtrie[p+1..n], p[1..n]
Exemple : Si le vecteur v contient les lettres B B C F F G, on devra trouver 63
positsup(v, ‘A’) = 0
positsup(v, ‘B’) = 2
positsup(v, ‘D’) = 0
positsup(v, ‘G’) = 6
positsup(v, ‘H’) = 0
Comme l’élément elem n’appartient pas obligatoirement au vecteur vtrie[1..n], on
cherche d’abord p tel que : vtrie[1..p]  elem < vtrie[p+1..n].
Ensuite, il suffit de vérifier si vtrie[p]=elem pour conclure que l’élément est
présent ou non dans le vecteur. On est donc ramené à un algorithme semblable au
précédent que l’on peut écrire avec une méthode séquentielle ou dichotomique.
Méthode séquentielle, on effectue un parcours de droite à gauche.
entier fonction positsup(d t v[], d entier n, d t elem)
debfonc
entier p, i;
p := 0;
si vtrie[1]  elem alors i := n; /* vtrie[1]  elem < vtrie[i+1..n] */
tantque vtrie[i] > elem faire i:=i-1;
finfaire;
si vtrie[i] = elem alors p := i;
finsi; finsi;
retourner p ;
64
finfonc
Méthode dichotomique
En utilisant les mêmes principes que précédemment, on obtient :
entier fonction positsupdicho(d t vtrié[], d entier n, d t elem)
debfonc
entier p, inf, sup, m;
p = 0;
si vtrié[n] = elem alors p := n;
sinonsi vtrié[n] > elem alors
inf := 1; sup := n;
tantque inf < sup faire
m := (inf + sup) div 2;
si vtrié[m]  elem alors inf := m + 1;
sinon sup := m;
finsi;
finfaire;
si (sup > 1) et (vtrie[sup-1] == elem) alors p:=sup–1;
finsi;
finsi;
retour p ;
finfonc; 65
Ecriture de la fonction tasser
Pour supprimer un élément d’indice p, il suffit de retasser tous les éléments ayant un
indice supérieur à p. Cela revient à considérer que le nouveau vecteur vtrié est la
concaténation de vtrié[1..place-1] et vtrié[place+1..n].
procédure tasser(dr t vtrié[], d entier n, d entier p)
// spécification(n > 0, p  [1..n])(retassement des éléments d’indice > p)
debproc
entier j;
pour j := p+1 haut n faire vtrié[j-1]:= vtrié[j];
finfaire;
n := n – 1;
finproc;

On remarquera que la translation ou décalage, à gauche, se fait à partir du premier


élément à gauche. Si le décalage était effectué à partir de la droite, toutes les valeurs de
v[place..n-1] seraient identiques et égales à v[n].

Exercice : on souhaite supprimer un sous-vecteur donné dans un vecteur. Exemple : on


veut supprimer la première occurrence du mot « très » dans la phrase « il fait très
beau aujourd’hui», ce qui donnera « il fait beau aujourd’hui ».
66
Exercice 2.16 : On souhaite remplacer un sous-vecteur donné dans un vecteur dans
un autre. Exemple : on veut remplacer la première occurrence du mot « très » dans
la phrase « il fait très beau et très chaud aujourd’hui » par le mot « assez ». On doit
donc obtenir : « il fait assez beau et très chaud aujourd’hui ».

Exercice 2.17: on souhaite maintenant remplacer toutes les occurrences d’un sous-
vecteur donné dans un vecteur par un autre. Exemple : on veut remplacer toutes les
occurrences de « très » dans la phrase « il fait très beau et très chaud aujourd’hui ».
On obtiendra donc « il fait assez beau et assez chaud aujourd’hui »

67