- EXAMEN - sujet A

Transcription

- EXAMEN - sujet A
DUT Informatique
Conception de structures de données
Année 2009/2010
- EXAMEN - sujet A - Aucun document autorisé - Calculatrices interdites - Durée : 2 heures NOM :
Prénom :
3 pts
Exercice 1.
Cellules en cycle �
En utilisant le type Cell défini pour les listes, on déclare le pointeur p1 comme suit :
Cell *p1=createCell(1,createCell(2,createCell(3,NULL))) ;
(((p1->next)->next)->next)=p1 ;
Remarque : dans la deuxième instruction, les flèches sont superflues.
1. Le résultat des 2 instructions précédentes provoque un cycle dans le chaı̂nage des cellules,
qui se traduit graphiquement ainsi :
p1
1
2
3
Représentez sur le dessin ci-dessous les pointeurs (flèches) permettant d’inverser les positions des cellules contenant 2 et 3, de telle sorte que les trois cellules forment toujours un
cycle. Numérotez-les suivant l’ordre dans lequel ils doivent être affectés pour que l’échange
fonctionne. Vous n’avez pas le droit d’ajouter de nouveaux pointeurs.
p1
1
2
3
2. Écrivez le code C correspondant, c’est-à-dire les instructions permettant d’inverser les positions des cellules contenant 2 et 3 (en conservant le cycle) sans déclarer de nouveaux
pointeurs ni modifier les champs elem des cellules.
1
4 pts
Exercice 2.
Tables de Hachage
Voici une liste de titres de films accompagnés de leur
date de sortie au cinéma. Dans cet exercice, vous manipulerez des tables de hachage permettant de stocker
ces informations. Les titres sont les clés et les données
sont les années de sortie des films. Dans tous les cas, la
fonction de hachage h est le nombre de caractères du
titre (espaces compris) modulo la longueur de la table.
Par exemple h(”Psychose”) vaut 8 pour une table de
longueur 10, mais seulement 3 pour une table de longueur 5.
1. Qu’est-ce qu’une collision dans une table de hachage ?
Le Parrain
Apocalypse Now
Pulp Fiction
Fight Club
Raging Bull
Taxi Driver
Casablanca
Le Parrain II
Blade Runner
Citizen Kane
Die Hard
Terminator 2
Orange Mécanique
Psychose
E.T.
(1972)
(1979)
(1994)
(1999)
(1980)
(1976)
(1942)
(1974)
(1982)
(1941)
(1988)
(1991)
(1971)
(1960)
(1982)
2. Représenter (verticalement) une table de hachage de longueur 10 contenant les films et leur
date de sortie insérés dans l’ordre donné ci-dessus. Les collisions sont résolues par chaı̂nage.
Une association peut être représentée comme un couple (film,date).
2
3. Représenter (verticalement) une table de hachage de longueur 20 contenant les films et leur
date de sortie insérés dans l’ordre donné ci-dessus. Les collisions sont résolues par adressage
ouvert en utilisant un sondage linéaire.
Que se passe-t-il si la table est de longueur 10 ?
4. Dans le cas du sondage linéaire, quelle est la complexité de la suppression d’un élément dans
une table de hachage de longueur n (les opérations que l’on compte sont les comparaisons
entre clés) ? Justifiez votre réponse.
3
5 pts
Exercice 3.
Listes de chaı̂nes de caractères
Dans cet exercice, on considère que le type Element correspond à char * afin de pouvoir
manipuler des listes de chaı̂nes de caractères (voir exemple en fin d’exercice).
1. Est-ce que l’on peut tester l’égalité de 2 chaı̂nes de caractères en utilisant == ? Expliquez
pourquoi.
2. La fonction que vous devez écrire prend en paramètre une chaine de caractères (terminée
par un ’\0’) constituée de mots (de longueur 20, au maximum) séparés par un caractère ’ ’.
Le caractère précédant le ’\0’ est aussi un espace.
Écrivez la fonction stringToList qui, étant donnée une telle chaı̂ne, renvoie une liste contenant tous les mots de la chaı̂ne (dans n’importe quel ordre). Faites attention à l’allocation
de la mémoire. Vous n’avez pas besoin de vérifier que la chaı̂ne est bien formée.
List stringToList(char *s)
4
3. Écrivez la fonction récursive nbVwords qui, étant donnée une liste de mots (obtenue avec
la fonction précédente, par exemple), renvoie le nombre de mots de la liste qui commencent
par une voyelle (a,e,i,o,u,y).
int nbVwords(List l)
4. Écrivez la fonction récursive beforeWord qui, étant donnée une liste de mots l et un
mot s, renvoie une liste contenant tous les mots de l qui se trouvent avant s dans l’ordre
alphabétique. Il n’est pas nécessaire de fabriquer des copies des mots.
List beforeWord(List l, char *s)
Exemple d’utilisation des fonctions de cet exercice :
List ls = stringToList("albatros chat pigeon iguane ornithorynque chien biche
corbeau otarie chameau souris cerf ");
printList(ls);
printf("\n nombre de mots commençant par une voyelle: %d\n",nbVwords(ls));
List beforeChasseur = beforeWord(ls,"chasseur");
printf("\n liste des mots avant chasseur dans la liste ls: ");
printList(beforeChasseur);
résultat :
[ cerf souris chameau otarie corbeau biche chien ornithorynque iguane pigeon chat albatros ]
nombre de mots commençant par une voyelle : 4
liste des mots avant chasseur dans la liste ls : [ cerf chameau biche albatros ]
5
10 pts
Exercice 4.
Expressions arithmétiques
Dans cet exercice, on s’intéresse aux expressions arithmétiques entières contenant des additions,
des soustractions et des multiplications binaires (2 opérandes). Nous allons utiliser différentes
structures de données pour les représenter et les calculer.
Pour coder ces expressions arithmétiques, on utilise des entiers pour les valeurs numériques et
les caractères ’+’, ’-’ et ’*’ pour les opérateurs binaires correspondants.
1. Commencez par écrire la fonction calculer qui calcule le résultat de l’opération a op b si
op est bien un des trois opérateurs binaires possibles et renvoie une erreur sinon.
int calculer(char op, int a, int b)
Toute expression arithmétique complètement parenthésée peut s’écrire sous la forme d’un arbre
binaire dont les éléments sont soit des opérateurs, soit des entiers.
Plus précisément, l’arbre associé à une expression arithmétique e est défini récursivement ainsi :
– si e est un nombre, alors l’arbre associé est une feuille dont la racine est e.
– sinon, e est de la forme (e1 ) op (e2 ) où op est un opérateur binaire et e1 et e2 sont des
expressions arithmétiques ; l’arbre associé est un arbre binaire dont la racine est op et ses
deux fils sont les arbres binaires associés à e1 et e2 .
Voici quelques exemples :
5
e1 = 5
le1 = [ 5 ]
8
+
*
4
*
-
2
e2 = 8-(4-2) = 6
attention: e = 8-4-2
10
+
3
1
4
5
1
e3 = ((10-3)*(1+1))+(4*5) = 34
le2 = [ '-' 8 '-' 4 2 ]
le3 = [ '+' '*' '-' 10 3 '+' 1 1 '*' 4 5 ]
Une expression arithmétique peut également être représentée de façon unique par la liste des
éléments de l’arbre en ordre préfixe (voir le1, le2 et le3 dans l’exemple ci-dessus).
! Pour toutes les complexités demandées dans cet exercice, les opérations élémentaires sont les affectations de pointeurs (déplacements dans les listes ou les arbres).
6
2. Les arbres représentant les expressions arithmétiques sont-ils des arbres complets (justifier) ?
Quels types d’éléments se retrouvent dans les feuilles ? Et dans les noeuds internes (ceux
qui ne sont pas des feuilles) ?
3. Pour coder les arbres et les listes associés aux expressions arithmétiques en C, on définit le
type des éléments afin qu’ils puissent être aussi bien des nombres que des opérateurs :
typedef struct{ int val ; char op ; }Element ;
On vous fournit également 2 fonctions permettant de fabriquer des éléments :
Element makeOp(char c){ Element res ; res.op=c ; return res ; }
Element makeVal(int v){ return (Element){v,’n’} ; }
Ecrivez les deux fonctions suivantes qui testent respectivement si e est un opérateur ou une
valeur :
int isOp(Element e)
int isVal(Element e)
4. Écrivez la fonction debutCerise, qui étant donnée une liste de nombres et d’opérateurs
(de type Element), renvoie 1 si les trois premières cellules de la liste forment une ”cerise”,
c’est-à-dire que le premier élément est un opérateur et les deux suivants sont des nombres.
Si ce n’est pas le cas, la fonction renvoie 0.
Par exemple, debutCerise(le3)=0 et debutCerise(queue(queue(le3)))=1.
7
int debutCerise(List l)
5. Écrivez la fonction trouverCerise, qui étant donnée une liste, renvoie un pointeur sur la
première cerise de la liste ou NULL s’il n’y en a pas.
Par exemple, trouverCerise(le3) renvoie un pointeur sur la cellule contenant le char ’-’.
Cell *trouverCerise(List l)
6. Écrivez la fonction reduireCerise, qui remplace les 3 cellules d’une cerise [op a b] dans
une liste par la valeur de l’expression arithmétique a op b. Le paramètre c est une pointeur
sur la première cellule de la cerise. Le reste de la liste ne doit pas être modifié.
(Indice : vous n’êtes pas obligé de créer une nouvelle cellule.)
Par exemple, si on affiche le3 après l’instruction reduireCerise(queue(queue(le3))), on
obtient le3 = [ ’+’ ’*’ 7 ’+’ 1 1 ’*’ 4 5 ].
void reduireCerise(Cell *c)
8
7. Que fait la fonction suivante (bt est un arbre d’expression arithmétique non vide) ?
int mystere(BTree bt){
List l=listPrefixOrder(bt); /* la liste des éléments de bt en ordre préfixe */
while(!isEmpty(queue(l))){
Cell *c=trouverCerise(l);
if(c==NULL) erreur("liste mal formée");
reduireCerise(c);
}
return elemVal(first(l));
}
8. Quelle est la complexité de mystere en fonction de la taille n de l’arbre (justifier) ?
9. Ecrivez la fonction récursive calculerExp qui calcule directement la valeur de l’expression
arithmétique représentée par l’arbre bt (sans passer par la liste en ordre préfixe !). On
suppose que bt est un arbre bien formé (qui correspond à une expression arithmétique).
int calculerExp(BTree bt)
10. Quelle est la complexité de calculerExp en fonction de la taille n de l’arbre (justifier) ? En
comparant avec la question 8, qu’en déduisez-vous ?
9

Documents pareils