Poly Algorithmique2
Poly Algorithmique2
Prof. H. Mraoui
1
Contents
I Rappel : Algorithmique . . . . . . . . . . . . . . . . . . . . . . . . 4
I.1 Qu'est ce qu'un algorithme . . . . . . . . . . . . . . . . . . . 4
I.2 Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
I.3 Saisie et achage . . . . . . . . . . . . . . . . . . . . . . . 7
I.4 Aectation . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
I.5 Opérateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
I.6 Tests et conditions . . . . . . . . . . . . . . . . . . . . . . . 8
II Fonctions et procédures . . . . . . . . . . . . . . . . . . . . . . . . 13
II.1 Fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
II.2 Procédures . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
III La récursivité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
III.1 Dénitions et Exemples . . . . . . . . . . . . . . . . . . . . 28
III.2 Récursivité terminale . . . . . . . . . . . . . . . . . . . . . 36
IV Enregistrements et chiers . . . . . . . . . . . . . . . . . . . . . . 38
IV.1 Structures et enregistrements . . . . . . . . . . . . . . . . . 38
IV.2 Fichiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
V La complexité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
V.1 Classes de complexités classiques . . . . . . . . . . . . . . . 55
V.2 Calcul de la complexité . . . . . . . . . . . . . . . . . . . . . 55
V.3 Exemples importants . . . . . . . . . . . . . . . . . . . . . . 57
VI Recherche d'un élément dans un tableau . . . . . . . . . . . . . . 60
VI.1 Recherche séquentielle . . . . . . . . . . . . . . . . . . . . . 60
VI.2 Recherche récursive . . . . . . . . . . . . . . . . . . . . . . 61
VI.3 Recherche Dichotomque . . . . . . . . . . . . . . . . . . . . 63
VII Tri des éléments d'un tableau . . . . . . . . . . . . . . . . . . . . 65
VII.1 Tri par sélection . . . . . . . . . . . . . . . . . . . . . . . . 65
2
VII.2 Tri rapide . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
VII.3 Tri à bulle . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
3
I Rappel : Algorithmique
I.1 Qu'est ce qu'un algorithme
Le terme algorithme est employé en informatique pour décrire une méthode de
résolution de problème programmable sur machine.
Un algorithme est une suite nie d'instructions à appliquer dans un ordre déter-
miné à un nombre ni de données pour arriver, en un nombre ni d'étapes, à un
certain résultat, et cela indépendamment des données.
Un algorithme décrit ce qui doit faire l'ordinateur pour arriver un but bien
précis. Ce sont les instructions qu'on doit lui donner. Donc l'algorithme est un
moyen pour le programmeur de présenter son approche d'un problème donné à
d'autres personnes, dans un langage clair et compréhensible par l'être humain.
Les opérations de base pouvant composer un algorithme sont décrites en pseudo-
langage. Il s'agit d'un langage informel proche du langage naturel et indépendant
de tout langage de programmation. Ce pseudo-langage sera ensuite traduit et codé
dans le langage de programmation désiré.
Les étapes de conception d'un algorithme :
Comprendre le problème.
Identier les données du départ (entrées) et celle(s) qu'il faut obtenir (sor-
ties).
Structurer les données (variables ou constantes, type...).
Lisibles (Compréhensible).
4
Concis (ne doit pas dépasser une page).
1. Début
2. Corps de l'algorithme
3. Fin
I.2 Variables
Les données manipulées dans un algorithme sont appelées des variables. Pour ex-
ister, une variable doit être déclarée, c'est-à-dire que vous devez indiquer au début
de l'algorithme comment elle s'appelle et ce qu'elle doit contenir. Les variables se
déclarent au début de l'algorithme, avant le programme lui-même mais après le
mot "Variable".
Variable /∗ Déclariation des variables ∗/
variable1 : type
variable2,variable3,... : type
Donc, chaque variable est dénie par :
Un nom qui le désigne et le distingue des autres variables.
5
Les entiers : nombres sans virgule, négatifs ou positifs.
6
I.3 Saisie et achage
Pour l'achage des données, on utilise le mot "Acher".
Pour inviter un utilisateur à rentrer au clavier une valeur, utiliser le mot "Lire".
Exemple :
Algorithme Lire
Variable x : Entier
Début
Acher("Saisir un entier x ")
Lire (x)
Acher (" x vaut :", x )
Fin
I.4 Aectation
Pour aecter une valeur à une variable, on écrit :
var ←− Valeur
L'aectation, notée par le symbole ←−, est l'opération qui évalue une expression
(constante ou une expression arithmétique ou logique) et attribue la valeur obtenue
à une variable.
I.5 Opérateurs
Les opérateurs permettent de produire un eet sur les objets de l'algorithme
(variables ou constante) en eectuant des calculs. des vérication d'égalités et
d'inégalités. Ils sont divisés en trois classe :
Les opérateurs arithmétiques
Opérateur Signication Syntaxe
+ addition x+y
- soustraction x−y
* multiplication x∗y
/ division x/y
mod modulo x mod y
div Division Euclidienne x div y
7
Exemple I.1
Exemples :
8
Algorithme Structure_Alrenative_1
Variable x : Entier
Début
Acher("Saisir un entier x ")
Lire (x)
Si (x > 0) Alors
Acher ( x,"est un nombre positif")
FinSi
Fin
Algorithme Structure_Alrenative_2
Variable x : Entier
Début
Acher("Saisir un entier x ")
Lire (x)
Si (x > 0) Alors
Acher ( x,"est un nombre positif")
Sinon
Acher ( x,"est un nombre négatif ou nul")
FinSi
Fin
Algorithme Maximum
Variable a ,b, max : Entier
Début
Acher ("Saisir deux entiers a et b ")
Lire (a,b)
Si (a > b) Alors
max ←− a
Sinon
max ←− b
FinSi
Acher ("le maximum de " , a , " et de " , b, " est : " , max)
Fin
9
Choix multiples
Suivant Cas est une instruction spécique pour le cas où il faudrait choisir
entre plusieurs possibilités. Voici sa syntaxe.
L'expression ne peut être qu'entière. L'expression est évaluée, puis on passe di-
rectement au "Cas" correspondant à la valeur trouvée. Le cas default est facultatif,
mais si il est prévu il doit être le dernier cas.
Lorsque la valeur de l'expression située après le Suivant Cas coincide avec
l'une des constante Cas, le programme ne se contente pas d'exécuter la ou les
instructions de la branche Cas concernée, mais exécute aussi toutes les instructions
suivantes, cela jusqu'à la n du Suivant Cas. Normalement, on ne veut pas
que soient exécutées les instructions des autres branches suivantes. Donc, il faut
une instruction spéciale pour assurer que seules les instructions souhaitées seront
eectuées. cette instruction est l'instruction Sortir.
L'instruction Pour :
Une boucle permet de répéter plusieurs fois un bloc d'instructions.
La structure Pour est une boucle qui teste une condition avant d'exécuter les
instructions qui en dépendent. Les instructions sont exécutées (répétées) tant que
la condition est remplie (Vraie). Voici la syntaxe de l'instruction Pour.
<Initialisation>
Pour expr_initiale allant de valeur1 à valeur2 faire
Bloc d'instructions
FinPour
Cette boucle est surtout utilisée lorsque l'on connaît à l'avance le nombre d'itérations
à eectuer. L'expr_initiale est eectuée une fois, en premier. Puis on teste la con-
10
dition. On eectue l'instruction puis l'incrémentation tant que la condition est
vraie.
Algorithme Factoriel
Variable
N : Entier
i : Entier
Fact : Entier
Début
Acher (" Saisir une valeur entière N > 0: ")
Lire (N)
Fact ←− 1
Pour i allant de 1 à N Faire
Fact ←− Fact*i
FinPour
Acher ("Le factoriel de ", N , " est : " , Fact)
Fin
L'instruction Tant que:
La boucle Tant que permet de faire répéter l'exécution d'instructions tant qu'une
certaine condition est remplie (Vraie). Avec la syntaxe :
<Initialisation>
Tant que Condition Faire
Bloc d'instructions
Bloc de contrôle
Fin Tant que
Algorithme ExempleTantque
Variable i : Entier
Début
i ←− 0
Tant que (i < 4) Faire
Acher(" Bonjour tout le monde ")
i ←− i + 1
Fin Tant que
Fin
11
L'instruction Faire jusqu'à :
Algorithme Somme
Variable
N, S, i: Entier Début
Acher ("Saisir une valeur entière positive :")
Lire (N)
S ←− 0
i ←− 0
Faire
i ←− i + 1
S ←− S + i
jusqu'à (i > =N)
Acher ("La somme : S = ", S)
Fin
12
II Fonctions et procédures
L'objectif de ce nouveau chapitre est de vous apprendre à organiser votre code
en modularisant à l'aide de ce qu'on appelle des fonctions ou procédures. les
fonctions font partie des aspects de traitement au même titre que les expressions,
les opérateurs et les structures de contrôle, que nous avons vues précédemment. En
règle générale, les fonctions opèrent sur des données, et en retour les données vont
inuencer nos fonctions. Jusqu'à maintenant les algorithmes que vous avez écrits
étaient constitués d'une séquence linéaire d'instructions sans organisation plus
globale et sans partages des tâches répétées. Par exemple, si la tâche consistant
à calculer le maximum de trois nombres comme le fait le code suivant devait être
exécuté plusieurs fois dans notre algorithme. Par exemple, calculer le maximum de
trois nombres à plusieurs endroits dans un plus gros algorithme. Que feriez-vous
? Vous pourriez être tenté de recopier le code autant de fois que nécessaire aux
endroits appropriés, mais évidemment ceci est très mauvaise solution. Il ne faut
jamais dupliquer de code en programmant. Ne soyez pas tenté par le copier-coller.
Ce que vous voudriez recopier doit être mis dans une fonction. Pourquoi ne jamais
dupliquer du code (copier/coller). Cela rend l'algorithme
Inutilement long
Dicile à comprendre
13
Si (a > b) alors
max2 ←− a
Sinon
max2 ←− b
FinSi
Si (c > max2) alors
max3 ←− c
Sinon
max3 ←− max2
FinSi
Problème :
Dès qu'on commence à écrire des programmes, il devient dicile d'avoir une vision
globale sur son fonctionnement :
Diculté de trouver des erreurs.
Redondance.
Maintenance.
Solution :
Lorsqu'un programme est très long, il n'est pas réaliste de tout programmer
d'un seul tenant. Le programme est décomposé en de plus petites parties (sous-
algorithmes) qui sont ensuite appelées au moment opportun par l'algorithme prin-
cipal.
Un programme complet est souvent forme une application. Une application est
composée de plusieurs parties fonctionnelles :
L'algorithme principal qui correspond du bloc principal d'instructions situées
sous le mot-clé Algorithme et Début.
Des sous-algorithmes chargés de divers rôles. C'est l'algorithme principal qui
se charge souvent d'appeler des sous-algorithmes.
14
II.1 Fonctions
Déclaration et dénition
Dénition II.1 Une fonction est un sous programme particulier, accomplissant
une tâche particulière et qui ne renvoie, par exemple dans l'algorithme principal,
qu'un et un seul résultat.
Pourquoi on l'utilise :
Décomposer l'algorithme en de parties appelées par le programme principal
ou un sous programme.
Éviter la répétition inutile les mêmes instructions plusieurs fois.
15
Fonction N om_de_f onction(Liste des paramètres) : type_de_retour
Variable /∗ Déclaration des variables ∗/
DébutFonction
Retourne (Résultat)
FinFonction
On utilise le mot clef "Retourne" pour dire que la fonction doit fournir le résultat
en retour.
Une fonction a trois facettes. Tout d'abord son prototype, qu'on peut voir
comme un résumé de ce que doit faire la fonction. Puisqu'il contient son nom,
ses paramètres qui correspondent aux valeurs dont a besoin la fonction pour pou-
voir fonctionner. Ainsi, que le type de la valeur que va fournir la fonction. Un
deuxième aspect, est la création eective de la fonction ou sa dénition qui com-
mence comme un prototype mais contient également de ce qu'on appelle le corps
de la fonction, qui est le code qui va être exécuté quand on utilise la fonction.
le dernier aspect est donc l'utilisation de la fonction ou son appel. on va utiliser
la fonction en lui donnant des valeurs eectives pour ces paramètres la fonction
va fournir une valeur qu'on va pouvoir aecter à une variable. En pratique le
programmeur-concepteur c'est dire la personne qui va écrire la dénition de la
fonction n'est pas forcément la même personne que le programmeur-utilisateur,
c'est dire la personne qui va utiliser la fonction. le programmeur-utilisateur n'a
pas à connaître le corps de la fonction . Tout ce dont il a besoin de connaître c'est le
prototype de la fonction, pour pouvoir l'appeler. Le prototype sert donc d'accord
entre le programmeur-utilisateur et programmeur-concepteur. Le programmeur-
utilisateur doit respecter le prototype de la fonction quand il appelle la fonction.
Le programmeur-concepteur va s'arranger pour que le prototype corresponde au
problème que le programmeur-utilisateur veut résoudre.
Donc, une fonction est une portion de programme réutilisable ou importante
en soi. Elle est caractérisée par
16
un corps : la portion d'algorithme à réutiliser ou mettre en evidence ;
Exemples :
Fonction qui renvoie le maximum de deux variables réelles :
17
Fonction Maximum2 (x : Réel, y : Réel) : Réel
Variable
max2 : Réel
DébutFonction
Si (x > y) alors
max2 ←− x
Sinon
max2 ←− y
FinSi
Retourne max2
FinFonction
Fonction qui renvoie le maximum de trois variables réelles :
Fonction Maximum3 (x : Réel, y : Réel, z: Réel) : Réel
Variable
max3 : Réel
DébutFonction
max3 ←− Maximum2(Maximum2(x ,y) ,z)
Retourne max3
FinFonction
Programme principal qui calcule le maximum de trois variables.
Algorithme Maximum_de_trois_variables
Variable
a,b, c,max3: Réel
Début
Acher (" Saisir 3 nombre réels : ")
Lire (a,b,c)
max3 ←− Maximum3(a,b,c)
Acher ("Le maximum est = ", max3)
Fin
18
moment où on appelle une fonction. Reprenons l'exemple du calcul le maximum
de trois nombres. Nous avons ici le cas d'un petit algorithme principal qui demande
à l'utilisateur d'introduire trois nombres, qui lit ces trois nombres depuis l'entrée
standard et qui ache le maximum de ces trois nombres. Le calcul du maximum
est réalisé au moyen d'un appel de fonction, et c'est les mécanismes appliqués par
cet appel de fonction que nous allons étudier.
Plaçons-nous dans une situation concrète. Que se passe-t-il lors de l'appel
suivant
19
résultant de l'appel de fonction, à savoir 5. Donc, à l'issue de la dernière étape la
variable "max3" ôte ici la valeur 5
Voici donc un petit résumé des diérentes étapes que nous venons d'examiner.
Lorsqu'un appel de fonction a lieu dans un algorithme, cinq étapes ont lieu
1. Les expressions passées en argument sont évaluées.
2. Les valeurs résultant de l'évaluation de ces expressions sont aectées aux
paramètres de la fonction.
3. Les paramètres de la fonction disposent désormais de valeurs concrètes avec
lesquelles la fonction peut travailler, le corps de la fonction s'exécute.
4. L'expression qui suit la première commande "Retourne" rencontrée à l'exécution
est évaluée.
5. Le résultat de cette évaluation est retourné comme résultat de l'appel, ce qui
en clair signie que cette valeur remplace désormais l'expression de l'appel.
Ceci est ce qui se produit dans le cas le plus général. Le cas où une fonction a
besoin, pour travailler, de données entrantes d'arguments, de paramètres entrants,
et fournit en sortie une valeur concrète. Il s'agit du cas le plus général. Il existe des
situations où ce schéma en cinq étapes est un petit simplié. Première situation,
il existe des cas où une fonction peut par exemple fournir un résultat en sortie,
mais n'a pas besoin de données entrantes pour pouvoir travailler. Donc, dans
ce cas de gure, puisqu'il n'y a pas besoin d'arguments en entrée, les étapes 1
et 2 n'ont pas lieu. Deuxième cas de gure, il peut se produire des situations
où une fonction réalise des traitement, mais ne fournit en sortie aucune valeur
concrète. Par exemple, une fonction peut réaliser un certain nombre d'achages
sur un terminal, mais ne pas fournir de valeur en sortie et dans ce cas de gure,
les étapes 4 et 5 n'ont pas lieu. Il existe un passage d'arguments particulier, que
l'on examine dans la programmation par C + +, qui s'appelle "le passage par
référence", où l'étape n'a pas lieu.
Considérons une fonction dénie comme suit
20
Fonction f(x : Entier) : Entier
Variable
....
DébutFonction
.... Retourne ...
FinFonction
Et voici enn une petite synthèse de l'appel fonctionnel.
z ←− f(y)
Remarque II.2 Lorsque on dit qu'une valeur est passée en argument d'une fonc-
tion, on signie que cette valeur est copiée dans un paramètre de la fonction.
Lorsque on dit qu'une fonction retourne une valeur y, on signie que l'expression
de l'appel de la fonction est remplacée par la valeur retournée.
En résumé, on doit d'abord identié le code qui devait être mis dans une
fonction. On extrait ce code pour pouvoir créer une nouvelle fonction que on a
nommée. On a identié ce dont avait besoin en entrée notre fonction pour pouvoir
fonctionner. On a aussi identié ce qui devait fournir notre fonction au reste de
l'algorithme. Maintenant, on peut remplacer la partie du code que nous avons
extraite, par un appel à notre fonction.
21
Variables locales et globales
L'endroit où les variables sont déclarées est très important. Selon cet endroit les
variables ont une portée diérente. La portée d'une variable est sa visibilité au
sein des diérentes parties du programme.
Dénition II.2 Les variables accessibles uniquement par le programme ou sous-
programme dans lesquels elles sont déclarées, sont appelées des variables locales.
Les variables locales de même nom n'ont aucun rapport entre elles. Elles sont
totalement indépendantes les unes des autres et aucune interaction entre elles n'est
possible. Donc, les variables locales peuvent donc parfaitement porter un même
nom. Du côté de la mémoire, le contenu des variables locales, qui portent le même
nom, est cloisonné et distinct, à des adresses (cases) diérentes.
D'autre part, il serait pourtant très pratique de pouvoir accéder à une vari-
able depuis n'importe quel endroit du programme, qu'il soit principal ou un sous-
programme. La portée d'une telle variable s'étendrait à tout le code. Ce type de
variable s'appelle une variable globale.
Les variables globales sont déclarées de cette manière (en dehors des sous-
programmes et du programme) :
22
Algorithme Exemple_passage_argument
Variable val : Entier
Début
val←− 1
f(val)
Acher("val= ", val)
Fin
Nous avons ici un petit algorithme principal dont la première instruction con-
siste à déclarer une variable "val" et l'initialiser avec la valeur 1. La seconde
instruction de cet algorithme appelle la fonction "f" qui est dénie ici, en lui
fournissant justement en argument la variable "val" que nous venons juste de dé-
clarer. Nous avons vu dans la partie précédente qu'au moment de cet appel il y
a une mise en correspondance entre l'argument d'appel de la fonction et la liste
des paramètres attendus par la fonction. Donc, ici nous avons deux entités, nous
avons une variable "x" utilisée par la fonction. Et il se trouve que la fonction
"f" altère cette variable, la modie en l'incrémentant d'une unité. la question que
nous nous posons maintenant est la suivante : Est-que "x" et "val" sont la même
zone mémoire ? Ce que veut dire concrètement que l'altération de "x" aura une
incidence sur "val" ou non. Il existe en programmation (langage Machine) deux
types de passage d'arguments. Le passage par valeur et le passage par référence.
Donc examinons sur le même exemple que vu précédemment ce qui se produit
dans l'un ou l'autre des cas. Donc, nous avons une petite fonction "f" qui prend
en paramètre un entier "x" elle ne retourne aucune valeur, d'ailleurs il n'y a pas
d'instruction "Retourne". Et ce que réalise cette fonction est d'incrémenter la
valeur de son paramètre "x" égal à "x+1". Si une fonction utilise le passage par
valeur, cela signie que le paramètre "x" de la fonction correspond à une zone
locale à la fonction. Ce qui signie que, au moment où on invoque la fonction en
lui passant en argument une variable, variable qui est dénie à un autre endroit du
programme comme par exemple ici, dans l'algorithme principal. Au moment où on
appelle la fonction, on va copier la valeur de la variable passée en argument dans
la zone locale à la fonction. Ce qui signie que lorsque on exécute ce traitement,
on va altérer la zone locale, la variable passée comme argument. ceci correspond
au passage par valeur. Pour indiquer que l'on souhaite utiliser un passage par
référence (par adresse) on spécie l'en-tête de la fonction d'une façon un peu par-
23
ticulière. Donc, on indique par un symbole particulier que la fonction "f" travaille
par référence (par adresse). Lorsqu'on dénit l'en-tête de la fonction de cette façon
là, on indique qu'au moment de l'appel "x" le paramètre de la fonction est une
référence (adresse) à la variable passée en argument, donc est simplement un autre
nom pour la variable "val" passée en argument. Ici la variable "val" valait donc 1,
et le nom "x" référence le même emplacement mémoire qui est le même que celui
occupé par "val". Et dans le cas présent, l'incrémentation de "x" va correspondre
aussi à une incrémentation de "val".
Donc, on distingue deux types de passages d'arguments
1. Lors d'un passage par valeur, la fonction va travailler sur une copie de
l'argument, ce qui signie que les modications que l'on fait à l'intérieur de
la fonction ne sont pas répercutées à l'extérieur de la fonction. On travaille
sur une zone locale, et l'argument n'est pas altéré.
2. lorsqu'on utilise le passage par référence, on indique que la variable locale
qui est passée en argument correspond à une référence sur l'objet associé
à l'argument lors de l'appel, et donc la diérence principale c'est que toute
modication eectuée à l'intérieur de la fonction se répercute aussi, est visible
aussi, une fois que la fonction a terminé son exécution. Le passage par
référence doit être explicitement indiqué en utilisant le symbole de l'adresse
après le type.
Pour résumer, lors d'un passage par valeur le paramètre de la fonction est une
zone locale à la fonction et toute altération n'a d'incidence que sur la zone locale,
ne se répercute pas sur la variable passée en argument. lors d'un passage par
référence, on indique que le paramètre de la fonction n'est autre qu'un nom sup-
plémentaire pour la variable passée en argument et toute altération du paramètre
altérera aussi la variable passée en argument. Si l'on prend maintenant notre petit
exemple introductif, nous avions donc un petit programme principal qui déclarait
une variable "val" donc le contenu est 1. Ensuite, il y avait un appel de fonction.
Si l'on examine, l'en-tête de la fonction, il n y a aucun symbole particulier, ce
qui signie que l'on utilise ici le passage par valeur. Puisqu'on utilise le passage
par valeur cela signie que "x", le paramètre de la fonction est une zone locale
et que donc la valeur de "val" est copiée à l'intérieur de "x". L'altération de "x"
n'a d'incidence que sue "x". Si on ache "x", on perçoit bien la modication de
24
"x", par contre lorsqu'on a terminé d'exécuter la fonction et qu'on fait acher la
valeur de "val", on voit bien que la valeur d'origine reste inchangée, et donc que
"val" vaut toujours "1".
II.2 Procédures
Dans certains cas, on peut avoir besoin de répéter une tâche dans plusieurs en-
droits, mais que dans cette tâche on ne calcule pas de résultats ou qu'on calcule
plusieurs résultats à la fois. Dans ce cas on ne peut pas utiliser une fonction, on
utilise une procédure.
Dénition II.3 Une procédure est un sous programme semblable à une fonction
mais qui retourne rien.
25
Procédure Nom_de_procédure(Liste des paramètres )
Variable /∗ Déclaration des variables ∗/
DébutProcédure
FinProcédure
Exemple:
26
Procédure Tri_décroissant (T : tableau[ ] d'entiers, taille : Entier)
Variable i, imax, a,k : Entier
DébutProcédure
Pour k allant de 0 à taille-2 Faire
imax ←− indice_val_max(T,taille,k)
a ←− T[k]
T[k] ←− T[imax]
T[imax] ←− a
FinPour
FinProcédure
Algorithme Programme_Pricipal
Constante n ←− 10
Variable
T : tableau[ 0..n-1] d'entiers
i: Entier
Début
Pour i allant de 0 à n-1 Faire
Lire (T[i])
FinPour
Tri_décroissant (T,n)
Pour i allant de 0 à n-1 Faire
Acher (T[i])
FinPour
Fin
27
III La récursivité
III.1 Dénitions et Exemples
Dénition III.1 Un sous-algorithme récursif est un sous-programme qui peut
s'appeler lui-même.
Remarque III.1 La récursivité peut être appliquée tant aux fonctions qu'aux procé-
dures.
28
Une fonction récursive contient un ou plusieurs paramètres qui évoluent lorsqu'
on appelle la fonction jusqu'à satisfaire un test qui nous permettra de sortir de la
fonction. La récursivité solutionne un problème en résolvant le même problème
mais donne une solution plus simple. Le processus de simplication se poursuit
jusqu'à l'atteinte d'un cas où la solution est connue.
Exemple :
Une factorielle est l'exemple classique d'application d'un algorithme récursif :
Si n = 0
(
1,
F act(n) := n! =
(n − 1)! ∗ n = F act(n − 1) ∗ n, Si n > 0.
L'algorithme principal pour calculer n! d'une manière récursive est donné par :
29
Algorithme Factorielle_d'_un_nombre
Variable n: Entier
Début
Acher ("Saisir un nombre entier : ")
Lire (n)
Acher ("La factorielle de ", n , " est " , Fact(n))
Fin
Exemple :
Calcul de la suite de Fibonacci :
Si n = 0 ou n = 1
(
1,
F ib(n) =
F ib(n − 1) + F ib(n − 2), Si n > 1.
30
Multiplier les éléments d'un tableau de taille n revient à multiplier T[ind_initial]
avec le produit du reste (tableau de taille n-1).
Le produit s'arrête si on vérie que ind_initial=ind_nal.
Tester si le tableau est vide (Arrêt des appels récursifs avec échec).
31
Fonction Recherche_Dicho(T : tableau[ ] d'entiers, ind_min: Entier,
ind_max : Entier, x: Entier) : booléen
Variable ind_moyen : Entier
DébutFonction
Si (ind_min>ind_max) Alors /* Point d'arrêt */
Retourne Faux
FinSi
ind_moyen ←− (ind_min+ind_max) div 2
Si ( T[ind_moyen]=x) Alors
retourne Vrai
Sinon
Si ( T[ind_moyen]>x) Alors
Retourne Recherche_Dicho(T,ind_min,ind_moy-1,x)
Sinon
Retourne retourne Recherche_Dicho(T,ind_moy+1,ind_max,x)
FinSi
FinSi
FinFonction
Algorithme Appel_Fonction_Recherche_Dicho
Constante n ←− 10
Variable
T : tableau[ 0..n-1] d'entiers
i: Entier
Début
Pour i allant de 0 jusqu'à n-1 Faire
Lire (T[i])
FinPour
Acher (" Donner un entier : " )
Lire (x)
Acher ("Résultat de la recherche est " , Recherche_Dicho(T,0,n-1,x))
Fin
Exemple : (Tri rapide)
L'algorithme de tri rapide, "quick sort" en anglais, est un algorithme de type
32
dichotomique. Son principe consiste à séparer l'ensemble des éléments en deux
parties.
Nous voulons donc un sous-algorithme récursif qui permet de trier un tableau.
Son principe est de parcourir le tableau T en la divisant systématiquement en
deux sous-tableaux T1 et T2. L'un est tel que tous ses éléments sont inférieurs
à tous ceux de l'autre tableau et en travaillant séparément sur chacun des deux
sous-tableaux en réappliquant la même division à chacun des deux sous-tableaux
jusqu'à obtenir uniquement des sous-tableaux à un seul élément.
Pour eectuer la séparation, une valeur pivot est choisie. Plus précisément,
pour partitionner un tableau en deux sous-tableaux T1 et T2 :
on choisit une valeur quelconque dans le tableau T (la dernière par exemple)
que l'on dénomme pivot,
puis on construit le sous-tableau T1 comme comprenant tous les éléments
de T dont la valeur est inférieure ou égale au pivot,
et l'on construit le sous-tableau T2 comme constitué de tous les éléments
dont la valeur est supérieure au pivot.
Appliquons cette démarche à l'exemple suivant : T = [ 4, 23, 3, 42, 2, 12, 45,
18, 38, 15 ]. Prenons comme pivot la dernière valeur pivot=15.
Balayage à gauche :
4 < 15 ⇒ il est dans le bon sous-tableau, on continue,
23 > 15 => il est mal placé il n'est pas dans le bon sous-tableau, on arrête
le balayage gauche,
tableau en cours de construction : [ 4, 23, 15].
Balayage à droite :
38 > 15 ⇒ il est dans le bon sous-tableau, on continue,
33
18 > 15 ⇒ il est dans le bon sous-tableau, on continue,
12 < 15 ⇒ il est mal placé il n'est pas dans le bon sous-tableau, on arrête le
balayage droit,
tableau en cours de construction : [ 4, 23, 15, 12, 45, 18, 38].
[ 4, 23, 15, 12, 45, 18, 38] ⇒ [ 4, 12, 15, 23, 45, 18, 38].
tableau en cours de construction : [4, 12, 3, 15, 23, 45, 18, 38]
42 > 15 ⇒ il est mal placé il n'est pas dans le bon sous-tableau, on arrête
de nouveau le balayage gauche,
tableau en cours de construction : [4, 12, 3, 42, 15, 23, 45, 18, 38],
On reprend le balayage droit à l'endroit où l'on s'était arrêté : [4, 12, 3, 42, 2,
23, 45, 18, 38, 15]
2 < 15 ⇒ il est mal placé il n'est pas dans la bonne sous-liste, on arrête le
balayage droit,
tableau en cours de construction : [ 4, 12, 3, 42, 15, 2, 23, 45, 18, 38],
Maintenant, on procède à l'échange 42 et 2 : [ 4, 12, 3, 2, 15, 42, 23, 45, 18, 38].
Donc, nous obtenons T1 = [4, 12, 3, 2] et T2 = [42, 23, 45, 18, 38]. Ainsi, à cette
étape voici l'arrangement de T :
34
Ensuite, les deux ensembles T1 et T2 sont triés séparément, suivant la même
méthode. Donc, le sous-algorithme de tri rapide est récursif.
35
Procédure TriRapide (T : tableau[ ] d'entiers, g: Entier, d: Entier)
Variable i,j,pivot : Entier
DébutProcédure
i ←− g
j ←− d
pivot ←− T[d]
Faire
Tant que T[i]<pivot Faire
i ←− i+1
FinTantque
Tant que T[j]>pivot Faire
j ←− j-1
FinTantque
Si (i<=j) Alors
echanger(T, i, j)
i ←− i+1
j ←− j-1
FinSi
Jusqu'à (i>j)
Si (g<j) Alors
TriRapide(T,g,j)
FinSi
quad Si (i<d) Alors
TriRapide(T,i,d)
FinSi
FinProcédure
36
valeur. Par exemple, l'appel récursif de la factorielle
Retourne n ∗ f (n − 1)
37
IV Enregistrements et chiers
IV.1 Structures et enregistrements
Type structuré
La faculté des sciences d'oujda organise les informations concernant la lière SMI-
S3 dans une liste identique à la suivante :
38
rechercher la liste des étudiants ayant validé le semestre,
Pour proposer une solution pratique, il faudrait une sorte de type particulier
qui pourrait regrouper en une seule liste des variables de types diérents. Ces
types existent. Ils s'appellent des structures et permettent de décrire des enreg-
istrement. Les enregistrements sont des structures de données dont les éléments
peuvent être de type diérent et qui se rapportent à la même entité. Les éléments
qui composent un enregistrement sont appelés champs.
Dénition IV.1 Une structure est un type de données déni par l'utilisateur et
qui permet de grouper un nombre ni d'éléments (ou champs) de types éventuelle-
ment diérents.
La syntaxe à utiliser pour dénir un nouveau type structuré :
Type
Structure nom_type
champ1: type_champ1
................
champn: type_champn
FinStructure
La structure est déclarée dans section particulière sous le mot-clé "Type",
entre les mots-clés Structure et FinStructure.
Chaque structure porte un nom. Ce nom sera utilisé pour déclarer des en-
registrements.
39
Une structure peut contenir 1 à n champs, du même type ou de types dif-
férents.
Une structure a un seul champ est en soi totalement inutile.
Exemple :
Type
Structure tetudiant
Num: Entier
Nom : chaîne de caractères
Moy : Réel
Oberv : chaîne de caractères
FinStructure
Une fois qu'on a déni un type structuré, on peut déclarer des variables enreg-
istrements exactement de la même façon que l'on déclare des variables d'un type
primitif.
Syntaxe :
Exemple :
Alors que les éléments d'un tableau sont accessibles au travers de leur indice, les
champs d'un enregistrement sont accessibles à travers leur nom, grâce à l'opérateur
'.', i.e., vous accédez aux champs d'un enregistrement en passant par le nom de
l'enregistrement et le nom du champ séparé par le caractère '.' , le point, selon la
forme suivant :
nom_enreg.nom_champ
40
étudiant.Num ←− 1
étudiant.Nom ←− "Idrissi Hassan"
étudiant.Moy ←− 12
étudiant.Obser ←− "Validé"
Donc, Les champs d'un enregistrement se manipulent exactement comme des
variables. Ils peuvent recevoir des valeurs et leur valeur peut être aectée à une
autre variable. Les champs peuvent être utilisés partout où les variables sont util-
isées, y compris comme paramètres de de sous-programmes, en saisie, en achage,
etc.
Exemple :
Prendre un catalogue de produits dans un magasin. Un article est décrit par une
référence, un nom et un prix.
41
Type
Structure tarticle
ref : chaîne de caractères
nom : chaîne de caractères
prix : Réel
FinStructure
Variable
article1, article2 : tarticle
réponse : chaîne de caractères
Début
Acher (" Référence du premier article ? ")
Lire (article1.ref)
Acher (" Nom du premier article ? ")
Lire (article1.nom)
Acher (" Prix du premier article ? ")
Lire (article1.prix)
Acher (article1.ref, article1.nom, article1.prix)
Acher (" Copier le premier article dans le second ? ")
Lire (réponse)
Si (reponse= "oui") Alors
article2 ←− article1
FinSi
article2.prix ←− 15.25
Si (article2.prix = article1.prix) Alors
Acher (" Les deux articles ont le même prix ")
FinSi
Fin
Imbrication d'enregistrements
Un type structuré peut être utilisé comme type pour des champs d'un autre type
structuré.
Exemple :
42
Type
Structure tfabricant
ref : chaîne de caractères
nom : chaîne de caractères
tel : chaîne de caractères
FinStructure
Type
Structure tarticle
ref : chaîne de caractères
nom : chaîne de caractères
prix : Réel
fab : tfabricant
FinStructure
Maintenant déclarez un enregistrement de type tarticle :
43
Pour accéder au tableau, on utilise la syntaxe suivante :
nom_enreg.Champ_t[indice]
Exemple :
Nous voulons connaître le nombre d'articles vendus sur les 12 mois de l'année.
Type
Structure bilanart
art : tarticle
vente : tableau[0..11] de réels
FinStructure
Puis, déclarer
bart: bilanart
Exemple :
44
Tableaux d'enregistrement (ou tables)
Il arrive souvent que l'on veuille traiter non pas un seul enregistrement mais
plusieurs. Par exemple, on veut pouvoir représenter plusieurs articles. On va
créer un tableau regroupant toutes les articles d'un catalogue. Il s'agit alors d'un
tableau d'enregistrements.
Soit la structure tarticle :
Type
Structure tarticle
ref : chaîne de caractères
nom : chaîne de caractères
prix : Réel
FinStructure
Vous voulez créer une table de dix articles :
IV.2 Fichiers
Un chier est un ensemble structuré de données de même type, nommé et enreg-
istré sur un support lisible par l'ordinateur (disque dur, disquette, ash disque,
45
CD Rom, ..etc). Un chier peut contenir des caractères (chier textes), des pro-
grammes, des valeurs (chier de données).
Organisation des chiers : un chier se distingue des autres par quelques
attributs dont son nom et sa catégorie. Ils se distinguent aussi entre eux par
l'organisation de leurs données ce qui dénit leur format.
Catégories de chiers : deux catégories de chiers sont distinguables :
Les chiers organisés sous forme de lignes de texte successives, qui s'appellent
des chiers texte. Cela signie vraisemblablement que ce chier contient le
même genre d'information à chaque ligne. Ces lignes sont alors appelées des
enregistrements.
Le second type de chier : il rassemble les chiers qui ne possèdent pas de
structure de lignes (d'enregistrement). Ces chiers contenant des données
variées dont des nombres représentés sous forme binaire. Ces chiers sont
appelés des chiers binaires.
Structure des enregistrements: les chiers peuvent être structurés en enreg-
istrement. Il y a deux grandes possibilités pour structurer ces enregistrements
:
La structure est dite délimitée ; Elle utilise un caractère spécial, appelé car-
actère de séparation ou délimitation, qui permet de repérer quand nit un
champ et quand commence le suivant.
Exemple :
"Idrissi1";"Hassane1";0123;"[email protected]"
"Idrissi2";"Hassane2";0456;"[email protected]"
"Idrissi3";"Hassane3";0789;"[email protected]"
"Idrissi4";"Hassane4";0978;"[email protected]"
46
quitte à être complété par des espaces.
Types d'accès
Le type d'accès est la manière dont la machine va pouvoir aller rechercher les
informations contenues dans le chier. On distingue :
47
Manipulation les chiers à accès séquentiel
Pour travailler avec des chiers, vous devez respecter un certain ordre. Il vous
faudra :
Ouvrir le chier : c'est-à-dire indiquer à quel chier vous voulez accéder.
Remarque IV.2 Si l'on veut travailler sur un chier, la première chose à faire
est de l'ouvrir. Cela se fait en attribuant au chier un numéro de canal. On ne
peut ouvrir qu'un seul chier par canal, mais quel que soit le langage, on dispose
toujours de plusieurs canaux. Donc, l'accès à un chier passe par l'utilisation d'un
canal. Un canal permet de faire transiter un ux d'informations d'un programme
vers un chier.
Remarque IV.3 La déclaration des chiers doit gurer avant la déclaration des
autres variables.
48
Si on ouvre un chier pour lecture, on pourra uniquement récupérer les
informations qu'il contient, sans les modier en aucune manière.
Si on ouvre un chier pour écriture, on pourra mettre dedans toutes les in-
formations que l'on veut. Mais les informations précédentes, si elles existent,
seront intégralement écrasées et on ne pourra pas accéder aux informations
qui existaient précédemment.
Si on ouvre un chier pour ajout, on ne peut ni lire, ni modier les informa-
tions existantes. Mais on pourra, comme vous commencez à vous en douter,
ajouter de nouvelles lignes.
Remarque IV.4 Il faut toujours fermer un chier à la n d'une session. Par
exemple :
Fermer nom_chier
Exemple :
Algorithme Ouvre
Variable
c : chier séquentiel
nom : chaîne de caractères
Début
nom ←− "Toto.txt"
Ouvrir nom dans c en Lecture
/* traitements */
Fermer c
Fin
49
Lire(nom_chier, variable)
Exemple :
Algorithme Lire_chier
Variable
c : chier séquentiel
Ligne, Nom, Prénom, Tel, Mail: chaîne de caractères
Début
Ouvrir "Toto.txt" dans c en Lecture
Lire (c,Ligne)
Nom ←− Milieu(Ligne, 1, 20)
Prénom ←− Milieu(Ligne, 21, 35)
Tel ←− Milieu(Ligne, 36, 45)
Mail ←− Milieu(Ligne, 46, 100)
Fermer c
Fin
Remarque IV.5 La fonction " Milieu" : permet d'extraire une chaîne de carac-
tères d'une longueur donnée, à partir d' une position donnée.
50
Algorithme Lire_chier
Type
Structure Personne
Nom, Prénom, Tel, Mail: chaîne de caractères
FinStructure
Variable
c : chier séquentiel
Ligne: chaîne de caractères
i: Entier
Base : tableau[ ] de Personne
Début
Ouvrir "Toto.txt" dans c en Lecture
i ←− -1
Tantque (FinFichier(c)=Faux) Faire
i ←− i+1
Taille(Base) ←− Taille(Base)+1
Lire (c,Base[i])
FinTantque
Fin
Écrire(nom_chier, ligne)
Avec,
Ajouter : Ajout utilise l'instruction Écrire, une fonction qui prend comme
paramètre le nom du chier et l'enregistrement (la ligne). La syntaxe est la suivante
51
Écrire(nom_chier, ligne))
Avec,
52
V La complexité
Quand on tente à résoudre un problème, la question qui se pose c'est le choix du
meilleur algorithme parmi les algorithmes qui permettent de décrire des méthodes
de résolution de ce problème.
Certains algorithmes sont complexes et le traitement peut nécessiter beaucoup
de temps et de ressources de machine, c'est qu'on appelle le "coût" (ecacité ou
complexité) de l'algorithme.
Le but de ce chapitre est de donner des outils pour comparer diérentes solu-
tions algorithmiques à un problème donné.
L'analyse de la complexité des algorithmes étudie formellement la quantité de
ressources en temps et en espace nécessitée par l'exécution d'un algorithme donné.
En général, le temps d'exécution sur machine dépend de
Le problème à résoudre.
L'algorithme de résolution.
L'expertise du programmeur.
L'habilité du programmeur.
La rapidité de la machine.
Le langage de programmation.
Le compilateur.
53
Remarque V.1 Le choix de l'unité de mesure ne dépend pas de la nature précise
des données mais de leur taille n. Donc, La complexité s'exprime en fonction de
la taille n des données.
Mesure de la complexité : On distingue plusieurs types d'analyses de complexité
:
Le Meilleur des cas (cas favorable).
Le pire cas est qu'on parcours tous les éléments de la liste et que l'élément
recherché ne s' y trouve pas.
Le moyen cas est que l'élément recherché se trouve à la 2ème, 3ème position
par exemple.
54
V.1 Classes de complexités classiques
On voit souvent apparaître les complexités suivantes :
O(1) : complexité constante.
O(n!) : complexité factorielle. Sur Intel Pentium 4 à 3.2 GHz, si vous traitez
20 données dans un algorithme de complexité O(n!) le temps d'exécution est
autour de 25 ans.
55
Instructions conditionnelles
Instruction itératives
Donc,
k
X
(Ci (n) + Ti (n)) + Ck+1 (n)
i=1
Théorème V.1
56
Maintenant, on donne un exemple d'évaluation de C(n) pour les fonctions
récursives.
Fonction FunctionRecursive (n : Entier) : ....
DébutFonction
Si (n>1) Alors
FunctionRecursive(n/2), coût C(n/2)
Traitement(n), coût T(n)
FunctionRecursive(n/2), coût C(n/2)
Donc, on a l'équation récursive suivante :
C(n) = 2 ∗ C(n/2) + T (n) + 1.
Par conséquent,
Si T (n) = 1 alors C(n) = O(n).
Si T (n) = n alors C(n) = C(n) = O(n log n).
57
Donc, la complexité est en O(log2 (n)).
Exponentiation rapide :
On veut calculer xn , n ∈ N mesure la taille de l'entrée. Pour cela on propose deux
méthodes récursives.
Méthode 1 :
si n = 0,
(
1,
x ∗ xn−1 , si n > 0.
Théorème V.3
La complexité de la méthode 1 est en O(n).
Démonstration :
Puisque
C(n + 1) = 1 + C(n).
Alors
C(n + 1) = 1 + C(n)
= 2 + C(n − 1)
= ...
= n + C(1).
Méthode 2 :
si n = 0,
1,
xk ∗ xk , si n = 2k et k > 0.
si n = 2k + 1.
x ∗ xk ∗ xk ,
Théorème V.4
La complexité de la méthode 2 est en O(log(n)).
Tri rapide :
58
Maintenant, on étudie la complexité de la fonction récursive "TriRapide", qui
est basée sur le paradigme diviser pour régner, pour trier un tableau T de taille
n. Supposons que le pivot, après le partitionnement, se trouve à la position p,
0 ≤ p ≤ n − 1, c'est-à-dire pivot = T [p]. La formule de récurrence suivante somme
respectivement le coût du calcul du partitionnement et du test, soit O(1) + O(n)
= O(n) et des deux appels récursifs :
En eet, posons n = 2k
C(n) = n + 2 ∗ C(n/2)
= 2k + 2 ∗ C(2k−1 )
= 2k + 2 ∗ (2k−1 + 2 ∗ C(2k−2 )) = 2k + 2k + 22 ∗ C(2k−2 )
= ...
= k ∗ 2k + 2k ∗ C(1) = O(n ∗ log2 (n)).
Dans le pire des cas, c'est à dire quand une des parties est vide et l'autre
contient n − 1 éléments. Dans ce cas, on a p = 0. Par conséquent,
En eet,
59
VI Recherche d'un élément dans un tableau
On suppose qu'on dispose d'un tableau et on se donne un élément quelconque et
on cherche si cet élément est dans le tableau ou non.
Nous voulons un sous_algorithme qui recherche si un élément x appartient au
tableau T . Si x appartient eectivement à T , l'algorithme achera Vrai, et Faux
sinon.
60
Algorithme Appel_Fonction_Recherche_seq
Constante n ←− 10
Variable
T : tableau[ 0..n-1] d'entiers
i: Entier
Début
Pour i allant de 0 jusqu'à n-1 Faire
Lire (T[i])
FinPour
Acher (" Donner un entier : " )
Lire (x)
Si (Recherche_Seq(T,n,x)=Vrai) Alors
Acher(x," appartient au Tableau")
Sinon
Acher(x," n'appartientpas au Tableau")
FinSi
Fin
61
Fonction Recherche_Rec(T : tableau[ ] d'entiers, taille : Entier, i : En-
tier, x : Entier) : booléen
DébutFonction
Si (i>taille-1) Alors
Retourne Faux
Finsi
Si (x=T[i]) Alors
Retourne Vrai
Sinon
Retourne Recherche_Rec(T,taille,i+1,x)
FinSi
FinFonction
Algorithme Appel_Fonction_Recherche_Rec
Constante n ←− 10
Variable
T : tableau[ 0..n-1] d'entiers
i: Entier
Début
Pour i allant de 0 jusqu'à n-1 Faire
Lire (T[i])
FinPour
Acher (" Donner un entier : " )
Lire (x)
Si (Recherche_Req(T,n,0,x)=Vrai) Alors
Acher(x," appartient au Tableau")
Sinon
Acher(x," n'appartientpas au Tableau")
FinSi
Fin
62
VI.3 Recherche Dichotomque
Attention : On utilise la technique de "Recherche Dichotomique" uniquement si
le tableau est déjà trié.
Tester si le tableau est vide (Arrêt des appels récursifs avec échec).
63
Fonction Recherche_Dicho(T : tableau[ ] d'entiers, ind_min: Entier,
ind_max : Entier, x: Entier) : booléen
Variable ind_moyen : Entier
DébutFonction
Si (ind_min>ind_max) Alors /* Point d'arrêt */
Retourne Faux
FinSi
ind_moyen ←− (ind_min+ind_max) div 2
Si ( T[ind_moyen]=x) Alors
retourne Vrai
Sinon
Si ( T[ind_moyen]>x) Alors
Retourne Recherche_Dicho(T,ind_min,ind_moy-1,x)
Sinon
Retourne retourne Recherche_Dicho(T,ind_moy+1,ind_max,x)
FinSi
FinSi
FinFonction
64
Algorithme Appel_Fonction_Recherche_Dicho
Constante n ←− 10
Variable
T : tableau[ 0..n-1] d'entiers
i: Entier
Début
Pour i allant de 0 jusqu'à n-1 Faire
Lire (T[i])
FinPour
Acher (" Donner un entier : " )
Lire (x)
Si (Recherche_Dicho(T,0,n-1,x)=Vrai) Alors
Acher(x," appartient au Tableau")
Sinon
Acher(x," n'appartientpas au Tableau")
FinSi
Fin
La complexité est logarithmique.
65
Écrire l'algorithme principal.
66
Algorithme Programme_Pricipal
Constante n ←− 10
Variable
T : tableau[ 0..n-1] d'entiers
i: Entier
Début
Pour i allant de 0 à n-1 Faire
Lire (T[i])
FinPour
Tri_décroissant (T,n)
Pour i allant de 0 à n-1 Faire
Acher (T[i])
FinPour
Fin
67
Procédure echanger(T : tableau[ ] d'entiers, a: Entier, b: Entier)
Variable temp : Entier
DébutProcédure
temp ←− T[a]
T[a] ←− T[b]
T[b] ←− temp
FinProcédure
68
Maintenant, on étudie la complexité de la fonction récursive "TriRapide", qui
est basée sur le paradigme diviser pour régner, pour trier un tableau T de taille
n. Supposons que le pivot, après le partitionnement, se trouve à la position p,
0 ≤ p ≤ n − 1, c'est-à-dire pivot = T [p]. La formule de récurrence suivante somme
respectivement le coût du calcul du partitionnement et du test, soit O(1) + O(n)
= O(n) et des deux appels récursifs :
En eet, posons n = 2k
C(n) = n + 2 ∗ C(n/2)
= 2k + 2 ∗ C(2k−1 )
= 2k + 2 ∗ (2k−1 + 2 ∗ C(2k−2 )) = 2k + 2k + 22 ∗ C(2k−2 )
= ...
= k ∗ 2k + 2k ∗ C(1) = O(n ∗ log2 (n)).
Dans le pire des cas, c'est à dire quand une des parties est vide et l'autre
contient n − 1 éléments. Dans ce cas, on a p = 0. Par conséquent,
En eet,
69
VII.3 Tri à bulle
Le tri à bulles est un algorithme de tri, dans un ordre croissant, qui consiste à
comparer deux éléments consécutifs dans le tableau T . S'ils sont en ordre croissant
ils restent à leur place, sinon ils sont échangés. Puis on recommence avec les
éléments suivants.
Implémenter une procédure Tri_a_bulles qui prend en argument un tableau
T et sa taille et qui permet de trier, dans un ordre croissant, les éléments de T .
Calculer la complexité temporelle de cette fonction.
70