éléments de correction

publicité
MI2E - 2 E A NNÉE
A LGORITHMIQUE G ÉNÉRALE
Vincent Mousseau
E XAMEN FINAL
2 Février 2006 - 2 heures
Aucun document autorisé
Exercice 1 :
On s’intéresse à la gestion informatique des réservations sur l’année d’une salle de conférences.
La salle ne peut être réservée que par journées complètes. Ainsi, une réservation peut être assimilée à
un intervalle de jours I = [u(I), v(I)], où u(I) est le jour d’arrivée des participants et v(I) le dernier
jour de la conférence.
Au cours de l’année, les clients potentiels envoient au gestionnaire de la salle des demandes de
réservations sous la forme d’un intervalle. Celui-ci doit vérifier qu’aucune demande antérieure n’intersecte cette demande. Dans ce cas, il peut accepter cette réservation. Il arrive, enfin, que des clients
annulent une réservation.
Il existe donc trois opérations de base à réaliser : Intersecte(I) est une fonction qui renvoie
Vrai lorsqu’il existe une réservation antérieure qui intersecte I. Ajouter(I) ajoute un intervalle
disjoint à l’ensemble des réservations. Supprimer(I) supprime une réservation.
a) On suppose que les réservations acceptées sont gérées à l’aide d’une liste simplement chaînée.
Donner les déclarations de ce type LISTINTERVAL. Indiquer la complexité des trois opérations
de base, lorsque la liste est quelconque, puis lorsque la liste est triée selon les u(I) croissant.
Justifier votre réponse.
b) On suppose maintenant que les réservations sont stockées dans un arbre binaire de recherche
et que la clé d’un intervalle I est u(I). Les sommets comporteront donc, en plus des champs
habituels fgauche, fdroit et pere, les champs u,v représentant les bornes de l’intervalle
associé au sommet. On notera ABRI le type correspondant. Donnez les déclarations de ce type.
c) Représentez graphiquement l’arbre binaire de recherche (de type ABRI) correspondant aux
réservations successives (dans l’ordre) [37,39], [3,7], [100,120], [50,68], [10,30], [1,2] et [8,9].
d) Ecrire l’algorithme Intersecte(I :intervalle, A :ABRI) et analyser sa complexité.
Exercice 2 :
On considère un type LISTENT représentant des listes simplement chaînées d’entiers. Dans tout
l’exercice, vous utiliserez, sans les re-écrire, les primitives sur les listes. Toutefois, pour chacune des
primitives utilisées, vous en rappellerez la définition précise.
a) Ecrire deux algorithmes, l’un récursif, l’autre itératif, permettant de calculer la somme des
éléments de la liste.
b) Ecrire l’algorithme itératif permettant de supprimer toutes les occurrences de l’entier n de la
liste L.
c) Ecrire l’algorithme itératif permettant de supprimer la première occurrence de l’entier n de la
liste L.
d) En supposant que les listes L1 et L2 ne peuvent pas comporter de doublons, écrire un algorithme récursif permettant de déterminer la liste L3 intersection des listes L1 et L2.
e) Analyser la complexité de l’algorithme de la question d) en fonction de la complexité des
primitives.
Exercice 3 :
Un graphe G = (X, Y ) est défini par un ensemble de noeuds X et un ensemble d’arcs Y (un arc
(x, y) est représenté dans le schéma ci-dessous par une flèche du noeud x au noeud y). S’il existe un
arc (x, y) dans le graphe G, on dit que y est successeur de x et que x est prédécesseur de y (dans
l’exemple, 5 est successeur de 1 et 1 est prédécesseur de 5). La figure ci-dessous représente le graphe
pour lequel X = {1, 2, 3, 4, 5, 6} et Y = {(1, 5), (1, 2), (1, 4), (2, 1), (2, 3), (2, 5), (5, 6), (6, 3)}.
1
2
3
4
5
6
Il est classique de représenter un graphe en, stockant pour chacun de ses noeuds x ∈ X la liste de ses
successeurs. Ainsi, pour l’exemple précédent, on a :
– successeurs(1) = (2, 4, 5),
– successeurs(2) = (1, 3, 5),
– successeurs(3) = successeurs(4) = (),
– successeurs(5) = (6),
– successeurs(6) = (3).
Dans cette perspective on considère les déclarations suivantes :
Max=100
lien=↑cellule
cellule=Enregistrement
noeud :entier
suiv :lien
Fin Enregistrement
Graphe=Enregistrement
taille :entier
tab :Tableau[Max] de lien
Fin Enregistrement
a) Représentez l’état de la mémoire lorsque le graphe ci-dessus est stockée dans une variable G de
type Graphe
b) Ecrire l’algorithme AJOUTARC(i,j,G) qui ajoute en O(1) un arc allant du noeud i au noeud
j dans le graphe G.
c) On peut stocker un graphe dans G une variable de type Graphe en considérant que les liste
chaînées correspondent aux listes de prédécesseurs (au lieu de liste de successeurs). Représentez l’état de la mémoire lorsque le graphe ci-dessus est stockée dans une variable G’ de
type Graphe en considérant que les listes chaînées représentent la liste des prédécesseurs des
noeuds.
d) On cherche à définir la représentation d’un graphe en terme de listes de prédécesseurs (cf. c)) à
partir de sa représentation en terme de liste de successeurs. Ecrire un algorithme qui, en O(m)
(m étant le nombre d’arc du graphe), calcule la représentation d’un graphe en terme de listes de
prédécesseurs à partir de sa représentation en terme de liste de successeurs. On pourra utiliser
l’algorithme AJOUTARC.
2
Exercice 1 :
a) On suppose que les réservations acceptées sont gérées à l’aide d’une liste simplement chaînée.
Donner les déclarations de ce type LISTINTERVAL. Indiquer la complexité des trois opérations
de base, lorsque la liste est quelconque, puis lorsque la liste est triée selon les u(I) croissant.
Justifier votre réponse.
Correction :
lien=↑cellule
cellule=Enregistrement
u,v :entier
suiv :lien
Fin Enregistrement
LISTINTERVAL=lien
Si la liste chaînée est quelconque et comporte n éléments, alors l’ajout d’un élément peut se faire
en complexité constante O(1) (ajout en tête). Mais pour recherche un élément ou comparer les
éléments avec un intervalle donné pour savoir s’ils l’intersectent, il faut parcourir entièrement la
liste, d’où une complexité en O(n).
Lorsque la liste chaînée est triée selon les u(I) croissants, dans le pire des cas, le paramètre I
d’Intersecte ou de Ajoute a une borne inférieure u(I) qui est supérieure à celle des autres intervalles ce qui nécessite le parcours complet de la liste. Il en est de même lorsque l’on supprime
le dernier intervalle. Les trois opérations ont donc une complexité en O(n).
b) On suppose maintenant que les réservations sont stockées dans un arbre binaire de recherche
et que la clé d’un intervalle I est u(I). Les sommets comporteront donc, en plus des champs
habituels fgauche, fdroit et pere, les champs u,v représentant les bornes de l’intervalle
associé au sommet. On notera ABRI le type correspondant. Donnez les déclarations de ce type.
Correction :
lien=↑cellule
cellule=Enregistrement
u,v :entier
fgauche,fdroit,pere :lien
Fin Enregistrement
ABRI=↑cellule
c) Représentez graphiquement l’arbre binaire de recherche correspondant aux réservations successives (dans l’ordre) [37,39], [3,7], [100,120], [50,68], [10,30], [1,2] et [8,9].
[37,39]
[3,7]
[1,2]
[100,120]
[10,30]
[8,9]
3
[50,68]
d) Ecrire l’algorithme Intersecte(I,A) et analyser sa complexité.
Intersecte(I :intervalle, A :ABRI) → booléen
Si (A=Null)
Alors Retourner(faux)
Sinon si (u(I)<a.u)
alors si (v(I)<a.u)
alors Retourner (intersecte(I,A.g)
sinon Retourner (vrai)
Fin si
sinon si (u(I)<a.v)
alors Retourner (intersecte(I,A.d)
sinon Retourner (vrai)
Fin si
Fin Si
Exercice 2 :
On considère un type LISTENT représentant des listes simplement chaînées d’entiers. Dans tout
l’exercice, vous pourrez utiliser, sans les re-écrire, les primitives sur les listes. Toutefois, pour chacune des primitives utilisées, vous en rappellerez la définition précise.
a) Ecrire deux algorithmes, l’un récursif, l’autre itératif, permettant de calculer la somme des éléments de la liste.
Correction :
somme-iter(L :Liste) → entier
somme ← 0
p ← Premier(L)
Tant que (p 6= Fin(L)) faire
somme ← somme+Acceder(p,L)
p ← Suivant(p)
Fin Tant que
retourner(somme)
considérer comme juste les deux versions suivantes :
somme-recu(L :Liste) → entier
Si (Premier(L) = Fin(L)) alors retourner(0)
alors retourner(0)
Sinon retourner(Acceder(p,L)+somme-recu(L↑.suiv)
Fin si
somme-recu(L :Liste) → entier
Si (Premier(L) = Fin(L)) alors retourner(0)
alors retourner(0)
L’ ← Supprimer(premier(L),L)
Sinon retourner(Acceder(p,L)+somme-recu(L’)
Fin si
4
b) Ecrire l’algorithme itératif permettant de supprimer toutes les occurrences n dans L.
Correction :
retire-tout-iter(c : caractère L :Liste) → Liste
p ← premier(L)
Tant que (p 6= Fin(L)) faire
Si (Acceder(p,L)=c)
alors Supprimer(p,L)
sinon p ← Suivant(p,L)
Fin si
Fin Tant que
retourner(L)
c) Ecrire l’algorithme itératif permettant de supprimer la première occurrence n dans L.
Correction :
inter(c : caractère L :Liste) → Liste
p ← premier(L)
Tant que (p 6= Null) et (Acceder(p,L)6=c) faire
p ← Suivant(p,L)
Fin Tant que
Si (p 6= Null) alors Supprimer(p,L)
retourner(L)
d) En supposant que les listes L1 et L2 ne peuvent comporter de doublons, écrire un algorithme
récursif permettant de déterminer la liste L3 intersection des listes L1 et L2.
Correction :
inter(L1,L2) → Liste
L3 ← Liste vide
p ← Premier(L1)
Tant que (p 6= Fin(L1)) faire
x ← Acceder(p,L1)
q ← Premier(L2)
Tant que (q 6= Fin(L2) et (Acceder(q,L2) 6= x) faire
q ← Suivant(q,L2)
Fin tant que
Si (q 6= Fin(L2)) alors Inserer(x,Fin(L3),L3)
p ← Suivant(p,L1)
Fin Tant que
retourner(L3)
e) Analyser la complexité de l’algorithme de la question d) en fonction de la complexité des
primitives.
Correction :
Supposons que chacune des primitives utilisées (Fin, Suivant, Premier, Inserer) sont en O(1)
(c’est le cas pour une mise en oeuvre par liste simplement chaînée). Le pire cas intervient lorsque
L1 et L2 n’ont aucun élément commun. Soit n1 et n2 la longueurs de L1 et L2. Pour chaque
élément de L1, il faut le comparer à tous les éléments de L2. On est donc en O(n1 n2 ).
5
Exercice 3 :
Un graphe G = (X, Y ) est défini par un ensemble de noeuds X et un ensemble d’arcs Y (un arc
(x, y) est représenté dans le schéma ci-dessous par une flèche du noeud x au noeud y). S’il existe un
arc (x, y) dans le graphe G, on dit que y est successeur de x et que x est prédécesseur de y (dans
l’exemple, 5 est successeur de 1 et 1 est prédécesseur de 5). La figure ci-dessous représente le graphe
pour lequel X = {1, 2, 3, 4, 5, 6} et Y = {(1, 5), (1, 2), (1, 4), (2, 1), (2, 3), (2, 5), (5, 6), (6, 3)}.
1
2
3
4
5
6
a) Il est classique de représenter un graphe en, stockant pour chacun de ses noeuds x ∈ X la liste
de ses successeurs. Ainsi, pour l’exemple précédent, on a :
– successeurs(1) = (2, 4, 5),
– successeurs(2) = (1, 3, 5),
– successeurs(3) = ()
– successeurs(4) = (),
– successeurs(5) = (6),
– successeurs(6) = (3).
Dans cette perspective on considère les déclarations suivantes :
Max=100
lien=↑cellule
cellule=Enregistrement
noeud :entier
suiv :lien
Fin Enregistrement
Graphe=Enregistrement
taille :entier
tab :Tableau[Max] de lien
Fin Enregistrement
Représentez l’état de la mémoire lorsque le graphe ci-dessus est stockée dans une variable G de
type Graphe
6
1
•
2
•
4
•
5
2
•
1
•
3
•
5
5
•
6
6
•
3
3
4
7
8
..
.
b) Ecrire l’algorithme AJOUTARC(i,j,G) qui ajoute en O(1) un arc allant du noeud i au noeud
j dans le graphe G.
Correction :
Ajoutarc(i,j :entier, G :Graphe)
Allouer(nv)
nv↑.suiv ← G.tab[i]
nv↑.noeud ← j
G.tab[i] ← nv
c) On peut stocker un graphe dans G une variable de type Graphe en considérant que les liste
chaînées correspondent aux listes de prédécesseurs (au lieu de liste de successeurs). Représentez,
en mémoire le graphe ci-dessus dans une variable de type Graphe en considérant que les listes
chaînées représentent la liste des prédécesseurs des sommets.
d) On cherche à définir la représentation d’un graphe en terme de listes de prédécesseurs (cf. c)) à
partir de sa représentation en terme de liste de successeurs. Ecrire un algorithme qui, en O(m)
(m étant le nombre d’arc du graphe), calcule la représentation d’un graphe en terme de listes de
prédécesseurs à partir de sa représentation en terme de liste de successeurs. On pourra utiliser
l’algorithme AJOUTARC.
Correction :
Listpred(G :Graphe) → Graphe
G’ ← Graphe vide
G’.taille ← G.taille
pour i=1 à G.taille faire
p ← G[i]
Tant que (p 6= Null) faire
Ajoutarc(p↑.noeud,i,G’)
Fin tant que
Fin pour
Retourner(G’)
7
1
•
2
2
•
1
3
•
2
4
•
1
5
•
1
6
•
5
•
6
•
2
7
8
..
.
8
Téléchargement