Académique Documents
Professionnel Documents
Culture Documents
Objectifs
Ce cours a pour but d'introduire un panorama des structures de données complexes en suivant l'approche
de la programmation procédurale.
Contenu
Note: un calendrier d'avancement dans le cours est disponible pour vous permettre de déterminer ce qui a
été abordé à chaque leçon.
● structures de données statiques, types abstraits, notion de pointeur,
❍ arbres ,
❍ graphes
Forme de l'enseignement:
Cours ex-cathedra, exercices, travaux pratiques
Evaluation:
● Contrôles continus :
❍ lundi 7 mai 2001 de 14h00 à 16h00, auditoire U600 (Uni Dufour, 1er sous-sol) - notes
Note: dans les deux formes d'évaluation des étudiants (contrôle continu ou examen écrit), la note sera
laissée en suspend jusqu'à avoir satisfait aux exigences des travaux pratiques (avoir 75% des TPs
acceptés par l'assistant).
Encadrement:
Bertrand Ibrahim (bur. 350), Wolfgang Müller (bur. 336), Yvan Petroff (bur. 306).
Documentation:
livre support de cours et liste d'ouvrages de référence. Liste des questions d'examens avec leurs corrigés.
Enregistrements sonores
B. Ibrahim
19.04.01
Structures de Données
Arbre lexicographique
Question posée au contrôle continu du 7 mai 2001
On a construit un arbre multiple d'ordre 26 permettant de stocker des mots d'un dictionnaire de façon que
les chemins partant de la racine représentent des mots du dictionnaire, de la façon suivante
Les arcs sont étiquettés avec les lettres de l'alphabet (on suppose ici que l'on ne tient pas compte des
accents sur les lettres) et les noeuds contiennent un booléen indiquant si le chemin de la racine à ce
noeud représente un mot complet ou pas (sur le dessin - = faux et * = vrai).
Sur la base des déclarations suivantes, écrivez une fonction Trouve indiquant si un mot se trouve dans le
dictionnaire.
type VersNoeud= ^Noeud;
Noeud= record
Desc: array['a'..'z'] of VersNoeud;
Complet: boolean;
end; { Noeud }
var Dico: VersNoeud;
Arbre de tri
Question posée au contrôle continu du 7 mai 2001
Complétez le code des procédures ajouteElement, sommeArbre et produitArbre. La procédure
accumulerArbre est facultative et vaut un bonus de 5 points.
{
Ajoutez le code necessaire
}
program completer;
type
pElement = ^TElement;
TElement = record
mContenu : integer;
mDroite,mGauche : pTElement;
end; { TElement }
{un type de FONCTION}
TFonction = function(x, y:integer):integer;
var
gRacine: pElement;
i,gSomme,gProduit, gNouveau : integer;
{
multiplier:
Cette fonction multiplie deux nombres entiers
parametres:
inX, inY: les deux nombres a multiplier
resultat:
inX*inY
}
function multiplier(inX,inY:integer):integer;
begin
multiplier:=inX*inY;
end; { multiplier }
{
ajouter:
Cette fonction ajoute inX a inY
parametres:
inX, inY: les deux nombres a additionner
resultat:
inX+inY
}
function ajouter(inX,inY:integer):integer;
begin
ajouter:=inX+inY;
end; { ajouter }
{----------------------------------------
gestion d'arbres
----------------------------------------}
{
creeElement_Contenu
parametres:
}
procedure creeElement_Contenu(inNouveauContenu : integer;
var outElement : pElement);
begin
new(outElement);
outElement^.mDroite:=nil;
outElement^.mGauche:=nil;
outElement^.mContenu:=inNouveauContenu;
end; { creeElement_Contenu }
{
Ajoute un element a l'arbre pour que l'arbre soit un
arbre de tri, les elements les plus petits a gauche.
parametres:
inNouveauContenu: le contenu de l'element a ajouter
inoutRacine: la racine de l'arbre a qui un element va etre
ajoute
}
procedure ajouteElement(inNouveauContenu:integer;
var inoutRacine: pElement);
begin
{QUESTION:
end; { ajouteElement }
{
Cette procedure calcule la somme de tous les elements dans
l'arbre et l'ajoute a inoutSomme
parametres:
var inoutSomme: le parametre d'entree et le resultat
var inRacine: la racine de l'arbre a traiter
}
procedure sommeArbre(var inoutSomme:integer;
var inRacine: pElement);
begin
{QUESTION:
AJOUTEZ LE CODE NECESSAIRE POUR QUE sommeArbre
FASSE CE QUI EST DIT DANS LE COMMENTAIRE.
end; { sommeArbre }
{
La procedure qui suit calcule le produit de tout les elements
dans l'arbre et le multiplie avec inoutProduit
parametres:
var inoutProduit: le parametre d'entree et le resultat
var inRacine: la racine de l'arbre a traiter
}
procedure produitArbre(var inoutProduit:integer;
var inRacine: pElement);
begin
{QUESTION:
AJOUTEZ LE CODE NECESSAIRE POUR QUE produitArbre
FASSE CE QUI EST DIT DANS LE COMMENTAIRE.
}
...
end; { produitArbre }
{
La procedure qui suit traverse l'arbre en-ordre et applique
inFonction au contenu de la racine et inoutAccumulateur
dans la maniere suivante:
parametres:
inoutAccumulateur: contient un nombre entier
apres l'appel il contient le resultat de l'appel
inFonction: contient une fonction de type TFonction;
inRacine: contient la racine d'un arbre de tri genere
par ajouteElement
1..m sont les numeros de visite, donc n_1 est visite avant n_2,
n_3 etc.
inoutAccumulateur=i,
inFonction=f,
inoutAccumulateur va contenir:
inoutAccumulateur=f(n_m,f(...,f(n_2,f(n_1,i))))
}
procedure accumulerArbre(var inoutAccumulateur : integer;
inFonction : TFonction;
var inRacine: pElement);
begin
{QUESTION FACULTATIVE:
AJOUTEZ LE CODE NECESSAIRE POUR QUE accumulerArbre
FASSE CE QUI EST DIT DANS LE COMMENTAIRE.
}
...
end; { accumulerArbre }
{
SI TOUS LES QUESTIONS SONT BIEN RESOLUES,
LE PROGRAMME DOIT AFFICHER TROIS FOIS
LES MEMES gSomme ET gProduit RESPECTIVEMENT
}
begin
gRacine:=nil;
gSomme:=0;
gProduit:=1;
{
cree arbre aleatoire de 4 elements
}
for i:=1 to 4 do begin
gNouveau:=random(10)+1;
ajouteElement(gNouveau, gRacine);
gSomme:= gSomme + gNouveau;
gProduit:= gProduit * gNouveau;
end; { for }
write('gSomme:'); writeln(gSomme);
write('gProduit:'); writeln(gProduit);
gSomme:= 0;
gProduit:= 1;
sommeArbre(gSomme, gRacine);
produitArbre(gProduit, gRacine);
write('gSomme:'); writeln(gSomme);
write('gProduit:'); writeln(gProduit);
gSomme:= 0;
gProduit:= 1;
accumulerArbre(gSomme, ajouter, gRacine);
accumulerArbre(gProduit, multiplier, gRacine);
write('gSomme:'); writeln(gSomme);
write('gProduit:'); writeln(gProduit);
end.
Solution
Structures de Données
Exercice suivant
Arbres + chaînes
Question posée à l'examen écrit du 4 octobre 2000
Soit une structure d'arbre de tri et une structure de chaîne bidirectionnelle représentées par les
déclarations suivantes:
PtrNoeudCh = ^NoeudCh;
NoeudCh = record
Element: pNoeudArbre;
Precedent, Suivant: PtrNoeudCh;
end; { NoeudCh }
Ecrivez une fonction "Chemin" qui prend en paramètre un arbre et une valeur et retourne en résultat une
chaîne pointant sur les éléments successifs de l'arbre qui auront été examinés pour trouver la valeur dans
l'arbre de tri:
function Chemin(MonArbre: pNoeudArbre; MaValeur: integer): PtrNoeudCh;
Il faut bien entendu traiter tous les cas particuliers, p.ex. si l'arbre est vide la chaîne sera vide, si la valeur
fournie n'existe pas dans l'arbre, il faudra retourner le chemin parcouru jusqu'à trouver que la valeur ne se
trouve pas dans l'arbre.
Solution
Exercice suivant
Listes
Question posée à l'examen du 4 octobre 2000
{
Voici un programme qui gère les listes à la façon de Scheme
}
program liste_scheme;
var i : integer;
gChaine : PPaireOuValeur;
lInteger^.mEstValeur:=true;
lInteger^.mValeur:=inPremier;
cons_integer_paire:=cons_paire_paire(lInteger,inSecond);
end; { cons_integer_paire }
lInteger^.mEstValeur:=true;
lInteger^.mValeur:=inSecond;
cons_integer_integer:=cons_integer_paire(inPremier,lInteger);
end; { cons_integer_integer }
{
QUESTION:
Ecrivez une fonction qui crée une liste à la façon de Scheme avec
les entiers dans l'intervalle de inBegin à inEnd.
cree_chaine(5,8) devrait retourner l'equivalent de (list 5 6 7 8).
cree_chaine(5,5) devrait retourner l'equivalent de (list 5),
cree_chaine(5,4) devrait donner nil comme resultat.
}
function cree_chaine(inBegin,inEnd : integer ):PPaireOuValeur;
var lLastCreated: PPaireOuValeur;
var i: integer;
begin
{
<VOTRE SOLUTION ICI>
}
end;
{
QUESTION:
begin
gChaine:=cree_chaine(1,10);
writeln('Chaine cree');
affiche(gChaine);
affiche(cree_chaine(5,8));
affiche(cons_paire_paire(cree_chaine(1,2),
cons_integer_integer(3,4)));
end.
Solution
Les B-Arbres
Question posée à l'examen du 4 octobre 2000
. Soit le B-arbre d'ordre 2 suivant, dessinez le B-arbre résultant de l'insertion de la valeur 25:
b. Avec le même B-arbre de départ qu'en (a), dessinez le B-arbre résultant de l'insertion de la valeur
20
c. Avec le même B-arbre de départ qu'en (a), dessinez le B-arbre résultant de l'insertion de la valeur
14
d. Avec le même B-arbre de départ qu'en (a), dessinez le B-arbre résultant de l'insertion de la valeur 6
e. Avec le même B-arbre de départ qu'en (a), dessinez le B-arbre résultant de la suppression de la
valeur 60
f. Avec le même B-arbre de départ qu'en (a), dessinez le B-arbre résultant de la suppression de la
valeur 63
g. Avec le même B-arbre de départ qu'en (a), dessinez le B-arbre résultant de la suppression de la
valeur 80
Solution
Structures de Données
Exercice suivant
Conversion arbre-chaîne
Question posée à l'examen écrit du 3 juillet 2000
Soit une structure d'arbre de tri et une structure de chaîne bidirectionnelle représentées par les
déclarations suivantes:
PtrNoeudCh = ^NoeudCh;
NoeudCh = record
Donnee: integer;
Precedent, Suivant: PtrNoeudCh;
end;
Ecrivez une fonction "Conversion" qui prend en paramêtre un arbre et retourne en résultat une chaîne
contenant les valeurs de l'arbre dans l'ordre croissant.
Solution
Exercice suivant
Exercice suivant
Arbre multiple
Question posée à l'examen écrit du 3 juillet 2000
Le langage XML est un langage de balises destiné à succéder au langage HTML. En simplifiant, on peut
dire qu'un document XML contient un élément racine. Cet élément racine est un élément complexe,
c'est-à-dire qu'il contient un ou plusieurs autres éléments. Chaque élément est soit du texte simple, soit un
élément vide composé uniquement d'une balise de la forme "<NomBalise />", soit un élément complexe
délimité, à son début, par une balise d'ouverture de la forme "<NomBalise>", à sa fin, par une balise de
fermeture de la forme "</NomBalise>" et contenant, entre deux, une succession d'éléments.
Un document XML peut être vu comme une structure arborescente du type arbre multiple. En supposant
que <X>Y</X> est représenté par
Exercice suivant
Exercice suivant
Listes
Question posée à l'examen du 3 juillet 2000
program SchemeList;
{
une structure qui peut contenir soit une valeur, soit une paire
}
type PSchemePaireOuValeur = ^TSchemePaireOuValeur;
TSchemePaireOuValeur = record
{ le record contient-il une valeur }
mEstValeur:boolean;
{
Une fonction qui cree une valeur
}
function creeValeur(inValeur : integer): PSchemePaireOuValeur;
var lResultat : PSchemePaireOuValeur;
begin
new(lResultat);
lResultat^.mEstValeur:=true;
lResultat^.mValeur:=inValeur;
lResultat^.mCar:=nil;
lResultat^.mCdr:=nil;
creeValeur:=lResultat;
end; { creeValeur }
{
Une fonction qui cree une paire
}
function creePaire(inCar,inCdr: PSchemePaireOuValeur):PSchemePaireOuValeur;
var lResultat : PSchemePaireOuValeur;
begin
new(lResultat);
lResultat^.mEstValeur:=false;
lResultat^.mValeur:=0;
lResultat^.mCar:=inCar;
lResultat^.mCdr:=inCdr;
creePaire:=lResultat;
end; { creePaire }
{
Une procedure pour afficher
}
procedure display(inPaire : PSchemePaireOuValeur);
{vous etes autorise(es) de mettre des variables ici}
begin
{Mettez votre code ici.
Les commentaires du programme principal vous indiquent ce que
la procedure Display est censee imprimer}
end;
begin
gSchemePaire:=creePaire(creeValeur(1),
creePaire(creeValeur(2),
creePaire(creeValeur(3),nil)));
display(nil) {donne "()"};
writeln;
display(gSchemePaire); {donne "(1 . (2 . (3 . ())))"}
gSchemePaire:=creePaire(creePaire(creeValeur(1),
creeValeur(2)),
creePaire(creeValeur(3),
creeValeur(4)));
writeln;
display(gSchemePaire); {donne "((1.2).(3.4))"}
end.
Solution
Exercice suivant
Graphe orienté
Question posée à l'examen écrit du 3 juillet 2000
L'on modélise un planning à l'aide des structures suivantes :
var
Debut: array [1..NbTaches] of Integer;
{ pour chaque tâche, le jour auquel elle est censée débuter }
Duree: array [1..NbTaches] of Integer;
{ la durée de chaque tâche, en nombre de jours }
Avant: array [1..NbTaches,1..NbTaches] of Boolean;
{ Avant[X,Y] vrai si la tâche X doit être achevée avant
le début de Y }
Écrire une procédure/fonction étudiant la cohérence de ces données, c.-à-d. :
1. vérifier que chaque tâche peut bien commencer au jour prévu, en tenant compte de la durée de ses
antécédentes;
2. si cela n'est pas vérifié, imprimer où se situe l'erreur, et quel est l'enchaînement des tâches
précédentes (depuis l'origine) à prendre en compte pour déterminer la date de début correcte ;
3. le cas échéant, proposer des améliorations locales du planning (quand une tâche pourrait
commencer plus tôt que prévu).
Il n'est pas demandé de modifier les données initiales, ni de réaliser plusieurs passes, chacune tenant
compte des corrections ( avancée ou retardement d'une tâche) de la précédente. Toutefois, s'il vous reste
du temps, cela pourra constituer un bonus...
Attention : il n'est pas spécifié que les tâches doivent s'effectuer l'une après l'autre, certaines peuvent
s'accomplir en parallèle. De même, la racine du processus n'est pas nécessairement unique (ni le graphe
connexe).
NB: Les variables Debut, Duree et Avant sont globales, et peuvent être utilisées comme telles dans votre
code.
Solution
Structures de Données
Graphes orientés
Question posée au contrôle continu du 22 juin 2000
Dessinez la fermeture transitive de chacun de ces quatre graphes:
Solution
Exercice suivant
Graphe orienté
Question posée au contrôle continu du 22 juin 2000
Il est aisément démontrable que lorsque l'on élève un nombre N au carré, les deux derniers chiffres du
résultat ne dépendent que des deux derniers chiffres de N. Ainsi, un nombre se terminant par 05 aura
toujours son carré se terminant par 25 ( 105->11025, 1905->3629025 ).
L'on peut donc définir une application reliant chacun des nombres de 00 à 99 à un autre de ces nombres,
correspondant à la valeur de son carré modulo 100. Ceci compose bien entendu un graphe orienté...
1. Considérant que chacun des noeuds est l'origine d'un et un seul arc, déterminez la structure de
données la plus simple adaptée à la représentation des arcs de ce graphe.
La propriété précédente amène logiquement ceci :
puisqu'un noeud a au plus un successeur (en fait, exactement un), l'on peut définir en suivant les arcs
(c'est-à-dire en réitérant le calcul) un chemin unique qui en est issu;
puisque tous les noeuds ont au moins un successeur, ce chemin aboutit obligatoirement à un cycle (le
nombre de noeuds étant limité).
L'on aimerait donc associer à chaque noeud la "séquence terminale" (cycle) qui lui correspond.
2. Écrivez une fonction imprimant, pour un noeud passé en paramètre, les noeuds composant le cycle
terminal qui lui est associé.
Pensez que chaque composante du graphe ne comporte qu'un seul cycle; rappelez-vous qu'un cycle est un
cas particulier de composante fortement connexe...
Solution
Exercice suivant
Exercice suivant
B-Arbres
Question posée au contrôle continu du 22 juin 2000
Complétez les procédures pour parcourir des B-arbres.
NB: Ces procédures sont sensées marcher avec tous les B-arbres possibles! Une solutions qui marche
juste avec l'arbre donné dans ce programme n'est pas considérée comme réponse à cette question (0
points). Sont marquées en caractères gras les parties du code qui sont essentielles à l'élaboration de la
solution.
program b_arbre;
const cTaillePage= ...;
{
Initialiser un element de page
}
procedure initialisePageElement(var outInitialise : TPageElement);
begin
outInitialise.mCle:=0;
outInitialise.mEnfant:=nil;
end;
{
Creer une page vide
outNouveauNoeud^.mPremierEnfant:=nil;
outNouveauNoeud^.mNombreElements:=0;
{
Ajoute une cle a une page existante
parametres:
inCle : la nouvelle Cle
inoutPage : la page a modifier
valeur de resultat:
true, si la page n'etait pas encore pleine
false, si la page etait pleine
}
function ajouteCleAPage(var inoutPage: TPage; inCle:integer)
:boolean;
begin
if (inoutPage.mNombreElements<cTaillePage) then begin
inoutPage.mNombreElements:=inoutPage.mNombreElements + 1;
inoutPage.mElements[inoutPage.mNombreElements].mCle:=inCle;
ajouteCleAPage:=true;
end
else ajouteCleAPage:=false;
end; { ajouteCleAPage }
{
Ajoute une pair de cle et pointeur a une page existante
}
function ajouteCleEtPointeurAPage(var inoutPage : TPage;
inCle :integer; inNouveauEnfant: PPage) :boolean;
begin
if (inoutPage.mNombreElements<cTaillePage)then begin
inoutPage.mNombreElements:=inoutPage.mNombreElements + 1;
inoutPage.mElements[inoutPage.mNombreElements].mCle:= inCle;
inoutPage.mElements[inoutPage.mNombreElements].mEnfant :=
inNouveauEnfant;
ajouteCleEtPointeurAPage := true;
end
else ajouteCleEtPointeurAPage := false;
end; { ajouteCleEtPointeurAPage }
{
Ajoute le premier pointeur a la page
}
function ajoutePremierPointeurAPage(var inoutPage: TPage;
inNouveauEnfant: PPage) :boolean;
begin
inoutPage.mPremierEnfant:=inNouveauEnfant;
ajoutePremierPointeurAPage:=true;
end; { ajoutePremierPointeurAPage }
{
Parcourir et afficher a l'ecran
}
procedure parcourirBArbreEnOrdre(inArbre: PPage );
{
ICI VOUS AVEZ LE DROIT DE METTRE DES VARIABLES
}
var i : integer;
begin { parcourirBArbreEnOrdre }
{METTEZ VOTRE SOLUTION ICI!}
end; { parcourirBArbreEnOrdre }
{
Parcourir EN ORDRE INVERSE et afficher a l'ecran
}
procedure parcourirBArbreEnOrdreInverse(inArbre: PPage );
{
ICI VOUS AVEZ LE DROIT DE METTRE DES VARIABLES
}
var i : integer;
begin { parcourirBArbreEnOrdreInverse }
{METTEZ VOTRE SOLUTION ICI!}
end; { parcourirBArbreEnOrdreInverse }
begin
creePage(gArbre);
creePage(gAuxiliaire);
gTest:= ajouteCleAPage(gAuxiliaire^,1);
gTest:= ajouteCleAPage(gAuxiliaire^,2);
gTest:= ajouteCleAPage(gAuxiliaire^,3);
gTest:= ajoutePremierPointeurAPage(gArbre^,gAuxiliaire);
creePage(gAuxiliaire);
gTest:= ajouteCleAPage(gAuxiliaire^,5);
gTest:= ajouteCleAPage(gAuxiliaire^,6);
gTest:= ajouteCleAPage(gAuxiliaire^,7);
parcourirBArbreEnOrdre(gArbre); { donne 1 2 3 4 5 6 7 }
writeln;
writeln;
parcourirBArbreEnOrdreInverse(gArbre); { donne 7 6 5 4 3 2 1 }
writeln;
end.
Solution
Exercice suivant
Structures de Données
Exercice suivant
Chaînes
Question posée au contrôle continu du 11 mai 2000
Complétez ce programme pour que toutes les fonctions fassent ce qui est dit dans les commentaires:
program chaines;
type
{
un pointeur sur des elements d'une chaine.
}
PElement = ^TElement;
{
un element d'une chaine:
}
TElement = record
{chaque element de la chaine contient un entier}
mContenu :
integer;
{
mSuivant: pointeur sur l'element suivant de la chaine,
nil pour le dernier element
}
mSuivant : PElement;
end;
var
gChaineDOrigine : PElement;
gChaineResultat : PElement;
{
VOUS ETES AUTORISES A AJOUTER QUELQUES FONCTIONS ICI
(Si vous voulez. Ce n'est pas forcement necessaire, mais
ca peut ameliorer la lisibilite de votre solution).
Mon conseil:
Copier la liste d'entree, en ajoutant les nouveaux elements
AU DEBUT de la nouvelle liste
}
end; { creeChaineInversee }
begin
{
VOUS N'ETES PAS AUTORISE A AJOUTER QUOI QUE CE SOIT ICI
}
new(gChaineDOrigine);
gChaineDOrigine^.mContenu:=2;
new(gChaineDOrigine^.mSuivant);
gChaineDOrigine^.mSuivant^.mContenu:=4;
new(gChaineDOrigine^.mSuivant^.mSuivant);
gChaineDOrigine^.mSuivant^.mSuivant^.mContenu:=6;
gChaineDOrigine^.mSuivant^.mSuivant^.mSuivant:=nil;
gChaineResultat:=creeChaineInversee(gChaineDOrigine);
Exercice suivant
Chaînes
Question posée au contrôle continu du 11 mai 2000
Étant données deux listes chaînées, comportant des entiers rangés selon l'ordre croissant, et définies
comme suit:
type pElement = ^tElement;
tElement = record
nombre : integer;
suivant : pElement;
end;
var debut1, debut2 : pElement;
Écrivez une fonction qui opèrera une fusion de ces deux chaînes dans une troisième, respectant
également l'ordre croissant de ses éléments, en enlevant les doublons.
Ainsi, à partir de:
liste 1 : 1 3 5 7 9
liste 2 : 1 2 4 7 11 16
l'on doit obtenir:
liste 3 : 1 2 3 4 5 7 9 11 16
Solution
Exercice suivant
Arbre de tri
Question posée au controle continu du 11 mai 2000
Soit un arbre de tri représenté par la structure suivante:
Exercice suivant
Tableaux
Question posée au contrôle continu du 28 février 2000
program question1;
begin
remplir
writeln('----------premiere partie');
affiche(1,1);
writeln('----------deuxieme partie');
affiche(1,3);
writeln('----------troisieme partie');
affiche(1,5);
writeln('----------fin')
end.
Qu'est-ce que ce programme affiche à l'écran?
Anneaux bidirectionnels
Question posée à l'examen écrit du 28 février 2000
Sur la base des déclarations suivantes:
type VersAnneau = ^ElementAnneau;
ElementAnneau = RECORD
Donnee: integer;
Preced, Suivant: VersAnneau;
END;
Ecrivez une procédure Tri(var Anneau: VersAnneau)qui trie par permutation les données d'un
anneau bidirectionnel. On supposera que les données sont tellement volumineuses que la permutation se
fera en manipulant les pointeurs et non pas en changeant le champ Info.
Solution
Arbres dépliables
Question posée aux examens écrits du 30 juin 1999 et du 28 février 2000
. Dessiner un arbre dépliable de tri, qui sera construit à partir des mots de la phrase suivante, en
traitant les mots en séquence de gauche à droite:
John also reviews the use of increasingly important industry specifications and
standards in platform or system issues.
b. Détruire l'élément qui contient le mot "increasingly" et dessiner l'arbre dépliable résultant.
Solution
Exercice suivant
B-Arbres
Question posée à l'examen du 28 février 2000
Soit un B-arbre construit à partir des déclarations suivantes:
const NodeSize = 4;
Exercice suivant
Exercice suivant
Anneaux bidirectionnels
Question posée à l'examen écrit du 11 octobre 1999
Etant donné les déclarations suivantes pour un anneau bidirectionnel:
type PtrNoeud = ^Noeud;
Noeud = RECORD
Donnee: integer;
Preced, Suivant: PtrNoeud;
END;
Exercice suivant
Exercice suivant
Arbre syntaxique
Question posée à l'examen du 11 octobre 1999
En supposantque l'on dispose des fonctions suivantes pour contruire une structure d'arbre:
function Arbre(ContenuRacine:char;
Gauche, Droit: PtrNoeud): PtrNoeud;
{ crée, par allocation dynamique, un nouveau noeud racine auquel
seront rattachés les sous-arbres gauche et droit fournis en
paramêtre, de façon à former un nouvel arbre }
1. Quelle sera l'expression, composée d'appels imbriqués aux fonctions Arbre et Feuille, qui
permettra de construire, en une seule instruction Pascal, l'arbre suivant:
2. Si, au lieu de créer une structure dynamique en mémoire, on voulait directement évaluer
l'expression arithmétique représentée par l'expression Pascal de la première partie de cette
question, quel devrait être l'entête et le code des fonctions Feuille et Arbre, en supposant qu'elles
retournent des valeurs entières
N.B.: on suppose que les opérateurs + et - peuvent être utilisés comme opérateurs monadiques,
c'est-à-dire ne portant que sur un seul argument (opérateurs de signe).
Solution
Exercice suivant
Exercice suivant
Listes
Question posée à l'examen du 11 octobre 1999
Indiquez ligne après ligne ce qu'imprime le programme suivant:
program listes;
var gDe,gA:integer;
begin
gDe:=1;
gA:=4;
writeln('Avant compter1');
compter1(gDe,gA);
gDe:=1;
gA:=4;
writeln('Avant compter2');
compter2(gDe,gA);
gDe:=1;
gA:=4;
writeln('Avant compter3');
compter3(gDe,gA);
gDe:=1;
gA:=4;
writeln('Avant compter4');
compter4(gDe,gA);
end.
Solution
Exercice suivant
Exercice suivant
Pour trouver les "distances" correspondant aux chemins les plus courts entre le noeud 1 et tous les autres
noeuds, exécutez l'algorithme suivant:
1. Soit une liste L et une liste A, toutes deux initialement vides.
2. Ajoutez à la liste L la paire (1;0) pour le noeud 1 et la "distance" 0.
Ajoutez pour chaque arc qui lie les noeuds x et y avec une "distance" z le triplet (x;y;z) à la liste A.
3. Retirez la paire (x;y) avec le deuxième élément (y) le plus petit de la liste L.
4. Pour chaque paire (x;y) avec le deuxième élément (y) le plus petit de la liste L, retiré à l'étape 3,
faites ce qui suit:
❍ Pour chaque arc (x,a,b) qui lie le noeud x à a avec une distance b ajoutez la paire (a;y+b) à
la liste L.
❍ Retirez (x,a,b) de A
5. Pour chaque paire (x;y) dans L: S'il existe une paire (x;z) dans L avec z plus petit ou égal à y
effacez (x;y) de L.
6. Continuez en 3 tant qu'il y a encore des éléments dans A
Donnez le contenu de A et de L après chaque pas.
Exercice suivant
Structures de données
Notes de l'examen du 30 juin 1999
Nom Note Remarque
Tiana Andriaharifara 0,0 absente
Patrick Arbus 0,5
José Barba 3,0
Ahcene Braik 2,0
Denis Bucher 4,5 note en attente des TPs
Hechmi Chnina 0,5
Baba Diao 0,0 absent
Yves Fomekong 3,5
Mouhamed Gningue 3,0
Nicolas Hurzeler 1,0
Sylvan Laurence 1,0
Thomas Pfund 4,0 note en attente des TPs
Bruno Rosa Martins 5,0
Cengiz Ulkü 0,5
Exercice suivant
Anneaux bidirectionnels
Question posée à l'examen écrit du 30 juin 1999
Sur la base des déclarations suivantes:
type VersAnneau = ^ElementAnneau;
ElementAnneau = record
Info: ...;
Precedent, Suivant: VersAnneau;
end; { ElementAnneau }
1. écrivez une fonction Pascal Inverse qui prend en paramètre un anneau bidirectionnel et retourne
comme résultat un pointeur vers un autre anneau qui a les mêmes éléments que le paramètre, mais
dans l'ordre inverse:
function Inverse(Ring: VersAnneau): VersAnneau;
2. écrivez une fonction Pascal EstInverse qui prend en paramètres deux anneaux bidirectionnels
et qui indique s'ils sont l'inverse l'un de l'autre:
function estInverse(Ring1, Ring2: VersAnneau): boolean;
Solution
Exercice suivant
Exercice suivant
Exercice suivant
procedure ChercheCoefficients;
begin { ChercheCoefficients }
{ ajustement des coefficients Coeff[i] et Taille }
...
end; { ChercheCoefficients }
begin { fabrique_fonction_de_H_code_parfaite }
ChercheCoefficients;
end. { fabrique_fonction_de_H_code_parfaite }
Ecrire la procédure 'ChercheCoefficients', permettant de fixer les coefficients ( Coeff[i] et
'Taille' ) de la fonction de H-code, afin qu'elle soit parfaite pour le remplissage du tableau avec les
mots du 'Vocabulaire'.
La solution doit impérativement minimiser le paramètre 'Taille'. Idéalement, la taille du tableau
serait égale au nombre de mots.
Il est conseillé d'utiliser la force brute pour résoudre ce problème. Il s'agira donc de tester toutes les
valeurs possibles pour les coefficients, jusqu'à tomber sur une fonction de H-code parfaite, ou que
l'espace des possibilités soit épuisé.
b. Le but de cette question est d'essayer de minimiser le nombre de collisions dans une structure de
données remplie à l'aide de l'algorithme de H-code. Nous avons:
❍ un tableau de strings appelé T, dont les éléments T[i] sont initialisés à '' (string vide)
❍ une fonction H(X) retournant l'indice dans le tableau T où l'élément X devrait être stocké.
❍ une fonction P(X) retournant la probabilité d'apparition de l'élément X sur l'ensemble des
données traitées.
L'optimisation proposée est la suivante :
Lors de l'insertion d'un mot X, si l'emplacement T[H(X)] est déjà occupé, le nouveau
mot X aura la priorité si sa probabilité est plus élevée que celle du mot dans
l'emplacement occupé.
Pour sa réalisation, il suffira de compléter la procédure ci-dessous, déjà vue au cours.
function Insert( X : string ) : integer;
var H0, hi : integer;
begin
H0 := H(X);
hi := H0;
while ( T[hi] <> '' ) and ( T[hi] <> X ) do begin
exercice suivant
Graphes orientés
Question posée au contrôle continu du 14 juin 1999
Soit un graphe défini sur la base des relations de dépendances suivantes concernant les ages respectifs
d'un groupe de personnes:
● Jean est plus jeune que Paul et Pierre, mais plus agé que Jeanne.
● Jacques est plus jeune que Jeanne et Pierre, mais plus vieux que Marie
● Pierre est plus jeune que Paul, mais plus vieux qu'André et Alice
● Yves est plus jeune que Paul, mais plus vieux que Claude et Alice
exercice suivant
Exercice suivant
B-Arbres
Question posée au contrôle continu du 14 juin 1999
Soit un B-Arbre d'ordre 1, c'est-à-dire que chaque page peut avoir soit 1 soit 2 données.
. Insertion: Insérez dans l'ordre indiqué les nombres: 1,2,3,6,7,4,5,8,9 dans le B-arbre et dessinez
après chaque insertion le B-arbre résultant.
b. Complexité d'espace: En supposant que chaque page occupe 512 octets (2 octets pour le nombre de
données dans la page, 4 octets par "pointeur" vers une autre page et 249 octets par donnée),
indiquez l'espace minimum et maximum nécessaires pour stocker 10'000 données dans ce B-arbre
d'ordre 1.
c. Profondeur: Quelle profondeur minimum et maximum aura le B-arbre avec 10'000 données.
Solution
Exercice suivant
exercice suivant
On décide de placer ces mots dans un tableau de 15 positions (indices 1..15) par la méthode de l'adresse
ouvert, en utilisant l'expression suivante pour traiter les collisions:
Hi(K) = [(H(K) + i*length(K)) mod 15] + 1, où i représente le nombre de collisions déjà subies lors de
l'insertion de K (i = 0 au début) et length(K) est la fonction qui retourne le nombre de caractères d'un
mot.
N.B.: Au cas où on essaye de placer un nouveau mot dans une position occupée par un mot ayant
lui-même été déplacé, on donnera la priorité au nouveau mot.
Dessinez le tableau résultant de l'insertion des mots énumérés ci-dessus en les prenant dans l'ordre où ils
sont écrits.
Solution
exercice suivant
Université de Genève
Faculté des Sciences 29 avril 1999
Département d'Informatique
Structures de Données
Contrôle Continu - Première épreuve
Rappel:
Ce contrôle continu est un examen. Vous n'êtes donc pas autorisés à communiquer avec d'autres
personnes par quelque moyen que ce soit (communication verbale, par support papier ou électronique,
courrier électronique, Web, espace disque), à l'exception des assistants chargé de la surveillance de cet
examen. Une vérifications des "logs" des serveurs Web, proxy et courrier électronique peut être effectuée
pour déterminer si quelqu'un a contrevenu à cette interdiction. Toute tricherie sera sanctionnée par une
note de 0 à l'examen final pour tous les contrevenants.
Vous êtes par contre autorisés à consulter toute la documentation que vous désirez, que ce soit le livre du
cours ou d'autres livres, vos notes personnelles, le site Web du cours (y compris les anciennes questions
d'examens et leurs corrigés), ou même des fichiers vous appartenant.
Recommandation:
Si vous n'arrivez pas à faire correctement fonctionner votre code pour une des deux questions, ne
restez pas bloqués dessus plus de deux heures pour avoir le temps de traiter l'autre question.
Questions
Question 1
Description
Ici vous trouvez le fichier qui contient un programme en Pascal.
Ce programme traite des chaînes bidirectionelles qui ont deux bouts facilement accessibles. Pour l'acces,
c'est a dire l'ajout et la suppression des elements et pour la construction et destruction des listes tout les
fonctions necessaires sont fournies avec les commentaires. Lisez et comprenez ces procedures. C'est
nécessaire pour le reste de cette question.
Question:
Completez maintenant les fonctions et procedures suivantes:
Cette fonction verifie si une chaine est un palindrome, c'est a dire si
estPalindrome_Chaine
le n-ième element est egale au (n-1)-avant-dernier-element
Cette fonction genère une nouvelle chaîne qui est exactement la
newReverse_Chaine
chaîne d'entrène lu a'l envers.
Cette fonction ajoute une chaine (deuxième paramètre) a la fin
d'une autre (premier paramètre). La chaîne qui était donée comme
ajouteApresChaine_Chaine
premier paramètre est entierement vide apres exécution de cette
fonction.
Conseil:
Lisez et comprenez vraiment les procédures données. Sinon la question devient beaucoup plus difficile.
Les arcs sont étiquettés avec les lettres de l'alphabet (on suppose ici que l'on ne tient pas compte des
accents sur les lettres) et les noeuds contiennent un booléen indiquant si le chemin de la racine à ce
noeud représente un mot complet ou pas (sur le dessin - = faux et * = vrai).
Sur la base des déclarations suivantes, écrivez une fonction Lecture qui construira la structure de
dictionnaire à partir d'un fichier Text où les mots sont fournis, un par ligne, et une fonction NbMots
comptant les mots du dictionnaire ayant une longueur donnée. Si la longueur est 0, il faut retourner le
nombre total de mots, quelle que soit leur longueur.
type VersNoeud= ^Noeud;
Noeud= record
Desc: array['a'..'z'] of VersNoeud;
Complet: boolean;
end; { Noeud }
var Dico: VersNoeud;
Indication: Nous vous recommandons de commencer par la fonction NbMots, en invoquant la fonction
"PseudoLecture" fournie, à la place de la fonction Lecture qui vous est demandée. Lorsque la fonction
NbMots sera au point et si vous en avez encore le temps, vous pourrez essayer de remplacer l'appel à la
fonction PseudoLecture par un appel à la fonction Lecture que vous aurez écrite.
Solution
Questions? Wolfgang Müller, Jean-Marc Küffer, Hidenori Yoshizumi, José Bernardo, Bertrand Ibrahim
ou plutôt oralement
Quelques photos sont disponibles, ainsi que le résultat de l'évaluation
Exercice suivant
Exercice suivant
Exercice suivant
Arbres dépliables
Question posée à l'examen écrit du 25 juin 1998
. Dessiner un arbre dépliable de tri, qui sera construit à partir des mots suivants, en prenant la
séquence de gauche à droite :
la, lumiere, du, jour, etait, si, forte, qu, une, illumination, soudaine, traversa, son,
esprit.
b. Détruire l'élément qui se trouve à la racine et dessiner l'arbre dépliable résultant.
Solution
Exercice suivant
Tables de décision
question posée à l'examen écrit du 25 juin 1998
Avec les déclarations suivantes définissant une table de décision ainsi qu'un arbre binaire ordonné,
écrivez une fonction «ConstruitArbre» qui convertisse une table de décision condensée (paramètre
d'entrée) en arbre binairee (paramètre de sortie) où les feuilles contiennent les indicateurs d'actions d'une
règle et les noeuds intermédiaires contiennent le texte des conditions de façon que l'arborescence
permette de sélectionner la règle qui s'appliquera à un cas donné.
Indication: il faut s'inspirer de l'algorithme de conversion d'une table de décision en cascade de tests,
mais construire une structure d'arbre au lieu de produire du code Pascal.
const MaxNbCond = ...;
MaxNbRegles = ...;
MaxNbActions = ...;
TableDecision = record
NbConditions: 1..MaxNbCond;
NbRegles: 1..MaxNbRegles;
ValCond: array[1..MaxNbRegles, 1..MaxNbCond] of Conditions;
NbActions: 1..MaxNbActions;
Agir: array[1..MaxNbRegles, 1..MaxNbActions] of boolean;
TxtConditions:array[1..MaxNbCond] of string[30];
TxtActions:array[1..MaxNbActions] of string[30];
end; { TableDecision }
PtrNoeud: ^Noeud;
Noeud = record
case Feuille: boolean of
false: (TxtCond: string[30];
Vrai, Faux: PtrNoeud);
true: (Actions: array[1..MaxNbActions] of boolean);
end; { Noeud }
Exercice suivant
Recherche B-Arbre
Question posée au contrôle continu du 15 juin 1998
Soit le B-Arbre d'ordre 1 suivant.
2.a)
Dessinez le B-arbre résultant de l'insertion de la valeur L dans le B-arbre ci-dessus.
2.b)
Dessinez le B-arbre résultant de la suppression de la valeur C dans le B-arbre ci-dessus.
2.c)
Dessinez le B-arbre résultant de l'insertion de la valeur Z dans le B-arbre ci-dessus.
2.d)
Dessinez le B-arbre résultant de la suppression de la valeur S dans le B-arbre ci-dessus.
2.e)
Dessinez le B-arbre résultant de la suppression de la valeur P dans le B-arbre ci-dessus.
Solution
Exercice suivant
La légende montre les types de terrains, et un nombre associé à chaque type qui indique la difficulté de sa
traversée. D'une case de la carte on ne peut accéder qu'à ses quatre voisines au nord, au sud, à l'est et à
l'ouest (le déplacement en diagonale est interdit).
Le concurrent est équipé d'un ordinateur, dans lequel la carte peut être stockée. On vous a demandé de
participer à l'écriture d'un programme qui lui permette de trouver le meilleur chemin dans cette situation,
c'est-à-dire le chemin du moindre coût.
3.a)
Comment représenteriez-vous l'ensemble des données fournies par la carte en utilisant un graphe?
(Donnez juste une courte explication - ne dessinez pas le graphe entier.)
3.b)
Quelle(s) structure(s) de données serait la(les) plus efficace(s) pour stocker les noeuds et arcs d'un
tel graphe? Ecrivez les déclarations Pascal qui définissent la(les) structure(s) que vous proposez.
3.c)
Quel algorithme de recherche utiliseriez-vous pour trouver le chemin le moins coûteux entre les
points A et B? Justifier votre réponse, en considérant d'autres algorithmes possibles.
3.d)
Ecrivez une description précise, en pseudo-code, de l'algorithme de recherche choisi.
Solution
Exercice suivant
Chaînes bidirectionnelles
Question posée au contrôle continu du 4 mai 1998
Supposons qu'un programme d'édition de texte utilise la structure de chaîne de strings suivante pour
représenter le texte en cours d'édition:
Exercice suivant
Chaînes mono-directionnelles
Question posée au contrôle continu du 4 mai 1998
Compléter le programme suivant en donnant le corps des procédures Insertion et Afficher, étant donnée
la structure de données citée par la suite
N.B. On conserve dans un tableau TabElement des chaînes avec leur taille respective.
Program Chaines;
{Programme qui permet d'affecter des valeurs aux chaînes qui se
trouvent dans un tableau et d'afficher le contenu de ce tableau}
CONST
MaxNbSommets = ...;
MaxNumChaine = ...;
TYPE
PtrNoeud = ^Noeud;
Noeud = RECORD
Donnee: integer;
Suivant: PtrNoeud;
END;
EltNoeud = RECORD
Taillechaine: Integer;
Chaine: PtrNoeud;
END;
Procedure Afficher;
{Procédure permettant d'afficher le contenu du champs "Donnee"
pour toutes les chaînes du tableau}
Solution
Exercice suivant
Exercice suivant
Anneaux bidirectionnels
Question posée au contrôle continu du 4 mai 1998
Avec les déclarations suivantes définissant un anneau bidirectionnel, on aimerait ranger une valeur dans
l'anneau où les données sont triées dans l'ordre croissant. Pour ce faire, écrire d'abord la procédure
d'insertion ChercherInsertion, qui permet de déterminer le noeud d'insertion pour une valeur donnée.
Puis écrire la procédure RangerValeur, qui fait appel à ChercherInsertion pour trouver où insérer la
nouvelle valeur et fait l'insertion proprement dite à l'aide de la procédure InsererNoeud que nous
supposerons déjà exister.
N.B. On suppose que le code de la procédure InsererNoeud est déjà fourni.
TYPE
PtrNoeud = ^Noeud;
Noeud = RECORD
Donnee : integer;
Precedent, Suivant: PtrNoeud;
END;
VAR
Entete : PtrNoeud; {Entête pour l'anneau}
AnneauVide : boolean; {Indicateur d'anneau vide}
Exercice suivant
Exercice suivant
Arbre syntaxique
Question posée au contrôle continu du 4 mai 1998
En supposantque l'on dispose des fonctions suivantes pour contruire une structure d'arbre:
function Arbre(ContenuRacine:char;
Gauche, Droit: PtrNoeud): PtrNoeud;
{ crée, par allocation dynamique, un nouveau noeud racine auquel
seront rattachés les sous-arbres gauche et droit fournis en
paramêtre, de façon à former un nouvel arbre }
. Quelle sera l'expression qui permettra de construire, en une seule instruction Pascal, l'arbre
suivant:
b. Donnez une déclaration possible pour le type Noeud ainsi que le code des deux fonctions
Feuille et Arbre.
Solution
Exercice suivant
exercice suivant
Structures de graphes
Question posée à l'examen écrit du 13 octobre 1997
En s'inspirant de la description de graphes non-orientés en termes de type abstrait vue au cours, nous
allons supposer que les types "Graphe" et "SerieDeSommets" ont été définis, sans que vous ne
connaissiez les détails de ces structures. Nous allons aussi supposer que les primitives suivantes sont à
votre disposition:
function Index(NomSommet: string; LeGraphe: Graphe): integer;
{ retourne un numero compris entre 1 et MaxNbSommets }
function NomSommet(IndexSommet: integer; LeGraphe: Graphe)
:string;
{ retourne le nom du sommet dont l'index est fourni }
function NombreDeSommets(LeGraphe: Graphe): integer;
{retourne le nombre de sommets effectifs composant le graphe}
function SommetsVoisins(IndexSommet: integer;
LeGraphe: Graphe): SerieDeSommets;
{ retourne une structure permettant de retrouver l'index des
voisins immédiats }
function PremierSommet(var UneSerie: SerieDeSommets): integer;
{ extrait le premier des sommets de la série et retourne son
index }
function SommetSuivant(var UneSerie: SerieDeSommets): integer;
{ extrait le prochain sommet de la serie & retourne son index}
function FinDeSerie(UneSerie: SerieDeSommets): boolean;
{ vrai s'il n'y a plus de sommet suivant dans la serie }
En utilisant ces primitives, tout en ignorant de quelle façon la structure de graphe est implantée, écrivez
une procédure de parcours en profondeur qui imprime les noms des sommets du graphe fourni en
paramêtre:
procedure DFS(LeGraphe: Graphe);
Solution
exercice suivant
Arbre lexicographique
Question posée au contrôle continu du 16 juin 1997
On veut construire un arbre multiple d'ordre 26 permettant de stocker des mots d'un dictionnaire de sorte
que les chemins partant de la racine représentent des mots du dictionnaire, de la façon suivante
Les arcs sont étiquettés avec les lettres de l'alphabet (on suppose ici que l'on ne tient pas compte des
accents sur les lettres) et les noeuds contiennent un booléen indiquant si le chemin de la racine à ce
noeud représente un mot complet ou pas (sur le dessin - = faux et * = vrai).
Sur la base des déclarations suivantes, écrivez une procédure imprimant touts les mots du dictionnaire
dans l'ordre croissant de longueur de mot et, pour des mots d'une même longueur, par ordre alphabétique:
exercice suivant
Hash-code
Question posée au contrôle continu du 16 juin 1997
Soient deux fonctions de hash-code H1 et H2 retournant les valeurs numériques suivantes:
mot: les examens du premier cycle pour lesquels il n y aurait qu
H1(mot): 15 6 11 1 3 6 2 5 7 15 2 9
H2(mot): 8 5 12 14 2 10 17 7 5 13 8 22
mot: un examen oral a subir sont admis si chaque note atteint quatre
H1(mot): 13 18 16 11 10 23 8 89 20 15 22
H2(mot): 9 6 1 4 3 7 13 6 17 2 12 16
On suppose que l'on utilise la méthode du chaînage externe à partir d'un tableau de 11 positions, en
utilisant H1 pour déterminer quelle chaîne utiliser et H2 pour trier les éléments d'une même chaîne
(c'est-à-dire qu'au sein d'une même chaîne, un mot mi sera placé avant un autre mot mj si
H2(mi)<H2(mj)). Dessinez le tableau des chaînes que l'on obtient, en mettant un mot et le résultat de H2
pour ce mot dans chaque élément de chaîne. Indiquez quelle expression basée sur H1 vous avez utilisée
pour le choix de la chaîne.
Solution
exercice suivant
exercice suivant
Tables de décision
Question posée au contrôle continu du 16 juin 1997
Avec les déclarations suivantes définissant des tables de décision, écrivez une procédure "Extension" qui
convertisse une table de décision condensée (paramètre d'entrée) en table étendue (paramètre de sortie):
TableDecision = record
NbConditions: 1..MaxNbCond;
NbRegles: 1..MaxNbRegles;
ValCond: array[1..MaxNbRegles,
1..MaxNbCond]
of Conditions;
NbActions: 1..MaxNbActions;
Agir: array[1..MaxNbRegles,
1..MaxNbActions]
of boolean;
TxtConditions:array[1..MaxNbCond]
of string[30];
TxtActions:array[1..MaxNbActions]
of string[30];
end; { TableDecision }
exercice suivant
Exercice suivant
Chaînes bidirectionnelles
Question posée au contrôle continu du 28 avril 1997
Supposons qu'un programme d'édition de texte utilise la structure de chaîne de strings suivante pour
représenter le texte en cours d'édition:
Exercice suivant
Exercice suivant
Chaînes bidirectionnelles
Question posée au contrôle continu du 28 avril 1997
Dans le programme suivant, vous disposez d'une chaine bidirectionnelle qui a un pointeur supplémentaire
(appelé ici S2):
procedure CreeS2(Depart:Ptr);
var Deplacement, i:integer;
Courant, Etape : Ptr;
FinEtape : boolean;
begin
Etape := Depart;
while Etape <> nil do begin
Deplacement := Etape^.info;
i := 1;
Courant := Etape;
FinEtape := false;
if Deplacement > 0 then begin
while (i <= Deplacement) and (not FinEtape) do
if Courant^.Suiv <> nil then begin
i := i+1;
Courant := Courant^.Suiv;
end {then}
else begin
Courant := nil;
FinEtape := true;
end; {if}
Etape^.s2 := Courant;
end {then}
else begin
Deplacement := -Deplacement;
while (i <= Deplacement) and (not FinEtape) do
if Courant^.Preced <> nil then begin
i := i+1;
Courant := Courant^.Preced;
end {then}
else begin
Courant := nil;
FinEtape := true;
end; {if}
Etape^.s2 := Courant;
end; {else}
Etape := Etape^.Suiv;
end; {while}
end; {CreeS2}
begin
initialise(LaChaine);
CreeS2(LaChaine);
P := LaChaine;
if P <> nil then
repeat
write(P^.info:1,'->');
P := P^.s2;
until P = nil;
else writeln('nil');
end.
où la procédure initialise(var Depart:Ptr); est utilisée pour faire les initialisations: créer la chaine et
initialiser le pointeur S2 à nil. Quel résultat sera affiché quand LaChaine pointe sur:
1. 2 -> 0 -> 1 -> 1 -> -3
2. 3 -> -1 -> 2 -> -1 -> -3
Solution
Exercice suivant
Exercice suivant
Arbre généalogique
Question posée au contrôle continu du 28 avril 1997
program arbre_genealogique;
type
Genre = (Masculin, Feminin);
Noms = array[1..MaxLongueurNom] of char;
ChaineFreresSoeurs = ^NoeudFrereSoeur;
PersonnePtr = ^Personne;
NoeudFrereSoeur = record
FrereOuSoeur: PersonnePtr;
FrereSoeurSuivant: ChaineFreresSoeurs;
end; {NoeudFrereSoeur}
Personne = record
Nom: Noms;
Prenom: Noms;
Sexe: Genre;
Epoux: PersonnePtr;
FreresEtSoeurs: ChaineFreresSoeurs;
Enfants: ChaineFreresSoeurs;
end; {Personne}
Exercice suivant
Exercice suivant
Exercice suivant
B-arbre
Question posée à l'écrit du 5 mars 1997
a) Soit le B-arbre d'ordre 2 suivant, dessinez le B-arbre résultant de l'insertion de la valeur 10
c) Avec le même B-arbre de départ qu'au point b, dessinez le B-arbre résultant de la suppression de la
valeur 55.
d) Soit le B-arbre d'ordre 2 suivant, dessinez le B-arbre résultant de l'insertion de la valeur 23
e) Avec le même B-arbre de départ qu'au point d, dessinez le B-arbre résultant de l'insertion de la valeur
4.
f) Avec le même B-arbre de départ qu'au point d, dessinez le B-arbre résultant de la suppression de la
valeur 15.
Solution
Exercice suivant
Exercice suivant
Chaînes bidirectionnelles
Question posée à l'examen écrit du 15 octobre 1996
Supposons qu'un programme d'édition de texte utilise la structure de chaîne de strings suivante pour
représenter le texte en cours d'édition:
Exercice suivant
Exercice suivant
Exercice suivant
Chaînes mono-directionnelles
Question posée à l'examen écrit du 9 juillet 1996
Soit une d`une chaîne monodirectionnelle avec des éléments entiers. Ecrivez une procédure Pascal qui
supprime à la fois le premier et le dernier élément de la chaîne, et ceci de manière répétitive, jusqu`a ce
qu'il ne reste qu'un seul élément, ou jusqu'a ce que la chaîne soit vide. La procédure donnera comme
résultat soit le dernier élément restant, soit le pointeur "nil".
Exemples:
1.
Dans ce cas, on supprime les éléments contenant les valeurs entièlres 2 et -1, et le résultat de la
procédure sera alors 3.
2.
Dans ce cas, on supprimera d'abord 2 et 5, ensuite 3 et -1, et le résultat sera alors "nil".
Solution
Exercice suivant
Réseau autoroutier
Soit un réseau autoroutier tel que celui-ci:
Exercice suivant
Exercice suivant
Anneaux bidirectionnels
Question posée au conctrôle continu du 17 juin 1996
On vous fournit un entier ainsi qu'un anneau bidirectionnel ayant comme éléments des entiers, pouvant
être négatifs. On vous demande de vous déplacer dans l'anneau à la position absolue donnée par l'entier
qui vous est donné. Là, vous prenez l'entier qui se trouve dans l'élément courant, vous supprimez
l'élément courant, et vous vous déplacez dans l'anneau, cette fois-ci de façon relative à la position
courante en fonction du nombre qui s'y trouvait. Vous devez répéter ces pas (prendre l'entier, supprimer
l'élément, se déplacer) jusqu'à ce que il n'y ait plus d'élément dans l'anneau, jusqu'à ce que vous sortiez
de l'anneau, ou bien jusqu'à ce que vous rencontriez la valeur 0 (zéro) dans l'anneau. (Attention, si vous
arrivez à la fin de l'anneau, il ne faut pas repartir depuis le début; de même, si on arrive au début,
il ne faut pas continuer à la fin).
On vous demande de fournir, dans un paramètre de sortie, le type d'arrêt et d'imprimer, dans la
procédure, les valeurs se trouvant dans les éléments visités. Utilisez les déclarations suivantes:
Dans le cas ci-dessus, si l'entier donné initialement est 1, on va à la première position, on prend 2, on
l'imprime et on supprime la première position de l'anneau. On se déplace en avant de 2 positions
(correspondant à la position 3 dans l'anneau initial), on prend -2, on l'imprime et on supprime l'é lément.
Ensuite il faut signaler la fin du programme, parce qu'avant l'élément que l'on vient de suprimer il n'y a
qu'un seul élément (contenant 4) et l'on ne peut donc pas se déplacer de 2 éléments en arrière.
Solution
Exercice suivant
Exercice suivant
Arbre de tri
Question posée au conctrôle continu du 17 juin 1996
a) Etant donné l'arbre de tri suivant, donnez une séquence possible des données fournies en entrée qui
aurait permis d'aboutir à l'arbre de tri en question:
Solution
Exercice suivant
Exercice suivant
Exercice suivant
Exercice suivant
B-arbre
Question posée au contrôle continu du 17 juin 1996
Dessinez le B-arbre résultant de la suppression de la valeur g dans le B-arbre d'ordre 1 suivant:
Solution
Exercice suivant
Exercice suivant
Chaînes bidirectionnelles
Question posée au conctrôle continu du 20 juin 1995
On dispose d'une chaîne bidirectionnelle avec des éléments contenant un nombre entier (pouvant être
négatif). Ecrire une procédure qui reçoive en paramètre la chaîne et un entier, puis se déplace à la
position dans la chaîne indiquée par cet entier et utilise le contenu de cette position (nombre entier
pouvant être négatif) pour se déplacer à nouveau, mais cette fois de manière relative à la position
courante. La procédure réitère ces déplacement relatifs en fonction de la nouvelle position courante
jusqu'à ce qu'un déplacement la fasse sortir de la chaîne ou que l'on revienne sur un élément déjà visité.
La procédure retournera alors le type d'arrêt et la position du dernier élément examiné.
Exemple:
Si l'on donne à la procédure le nombre 1, elle ira examiner le 1er élément de la chaîne, qui
contient la valeur 2. Elle ira ensuite examiner la position 3 (position courante=1,
déplacement relatif=2), puis la position 2 (position courante=3, déplacement relatif=-1) et
s'arrêtera là car la prochaine position serait 6 et que la chaîne est de longueur 4.
N.B.: En plus de la procédure, donner les déclarations de type nécessaires pour la structure de chaîne.
Solution
Exercice suivant
exercice suivant
Arbre lexicographique
Question posée au contrôle continu du 20 juin 1995
On veut construire un arbre multiple d'ordre 26 permettant de stocker des mots d'un dictionnaire de sorte
que les chemins partant de la racine représentent des mots du dictionnaire, de la façon suivante
Les arcs sont étiquettés avec les lettres de l'alphabet (on suppose ici que l'on ne tient pas compte des
accents sur les lettres) et les noeuds contiennent un booléen indiquant si le chemin de la racine à ce
noeud représente un mot complet ou pas (sur le dessin - = faux et * = vrai).
En utilisant la possibilité que donne le langage Pascal d'indexer un tableau de pointeurs à l'aide d'un
intervalle de caractères afin de représenter les étiquettes des arcs, donner:
a) les déclarations de types et de variables nécessaires à la construction d'un tel dictionnaire,
b) une fonction booléenne ayant pour paramètre le dictionnaire et un string et indiquant si le mot contenu
dans le string se trouve dans le dictionnaire,
c) une procédure imprimant touts les mots du dictionnaire dans l'ordre alphabétique.
Solution
exercice suivant
Exercice suivant
Exercice suivant
begin
fin := false;
while not fin do begin
if Debut = nil then begin {chaine vide}
fin := true;
Resultat := nil;
end
else if Debut^.suivant = nil then begin {un seul element}
Resultat := Debut;
fin := true;
end
else begin
{on efface le premier element}
courant := Debut;
Debut:= Debut^.suivant;
dispose(courant);
if Debut^.suivant = nil then begin {il y avait 2 el}
dispose(Debut);
Resultat := nil;
fin := true;
end
else begin {plus de 2 elements}
courant := Debut; { on est sur qu'il n'est pas nil}
while courant^.suivant^.suivant <> nil do
courant := courant^.suivant;
dispose(courant^.suivant);
courant^.suivant:=nil;
{c'est bien necessaire afin d'indiquer la fin de la chaine}
end; {else/begin}
end; {else/begin}
end; {while}
end; {Suppression}
{ version recursive }
function Trouve(Mot: string; Dico: VersNoeud): boolean;
begin { Trouve }
if Dico=nil then Trouve := false
else if Mot = '' then Trouve := Dico^.Complet
else Trouve := Trouve(Dico^.Desc[Mot[1]],
copy(Mot,2,length(Mot)-1));
end; { Trouve }
{ version non-recursive }
function Trouve(Mot: string; Dico: VersNoeud) : boolean;
var i: integer;
Courant: VersNoeud;
begin { Trouve }
i:=1;
Courant := Dico;
while (i<=Length(Mot)) and (Courant<>nil) do begin
Courant := Courant^.Desc[Mot[i]];
i := i+1;
end; { while }
if Courant=nil then Trouve := false
else Trouve := Courant^.Complet;
end; { Trouve }
procedure ajouteElement(inNouveauContenu:integer;
var inoutRacine: pElement);
begin
if (inoutRacine=nil)then begin
creeElement_Contenu(inNouveauContenu, inoutRacine);
end
else begin
if (inoutRacine^.mContenu < inNouveauContenu) then begin
ajouteElement(inNouveauContenu,inoutRacine^.mGauche);
end
else begin
ajouteElement(inNouveauContenu,inoutRacine^.mDroite);
end;
end;
end; { ajouteElement }
. La valeur 25 devrait normalement venir dans la deuxième feuille à partir de la gauche. La page en question étant
pleine, on examine les pages adjacentes pour voir s'il y a de la place pour opérer une "roccade". En l'occurrence, la
voisine de droite a une place libre. La valeur 40, de la page parente, va donc occuper cette place libre et est
remplacée par la valeur 30, qui est la plus grande valeur de la page devant subir l'insertion. La feuille qui contenait
la valeur 30 a maintenant une place libre permettant d'accueillir la valeur 25.
c. La valeur 14 devrait normalement venir dans la feuille qui se trouve à l'extrème gauche. La page en question étant
pleine et sa voisine aussi, il faut scinder la page en deux et insérer l'élément milieu (9) dans la page parente
d. La valeur 6 devrait normalement venir dans la feuille qui se trouve à l'extrème gauche. La page en question étant
pleine et sa voisine aussi, il faut scinder la page en deux et insérer l'élément milieu (6) dans la page parente. C'est
donc la nouvelle valeur qui vient s'insérer dans la page parente.
e. Pour supprimer la valeur 60, il faut la remplacer par la plus grande valeur du sous-arbre gauche ou la plus petite
valeur du sous-arbre droit. Ici, nous avons choisi la valeur 51 (plus grande valeur du sous-arbre gauche). La feuille
qui contenait la valeur 51 a encore 2 éléments, ce qui est suffisant pour ne pas nécessiter plus d'opérations.
Par contre, si nous choisissons de remplacer la valeur 60 par la plus petite valeur du sous-arbre droit (63), la feuille
contenant 63 tombera en dessous du seuil minimum (il ne restera que la valeur 70). Comme sa voisine de droite est
au seuil minimum, on ne peut pas lui emprunter d'élément. Il faut donc combiner la page ne contenant plus que 70
avec sa voisine de droite contenant 76 et 79, en absorbant au passage la valeur de la page parente (75) qui se trouve
entre les deux pages à combiner. On obtient ainsi une page contenant 70, 75, 76 et 79. Mais la page parente ne
contient plus que la valeur 80. Sa voisine de gauche étant au seuil minimum (valeurs 15 et 40), il faut combiner les
deux pages en absorbant la valeur de la page parente (63) qui se trouve entre elles. On obtient ainsi une page
contenant 15, 40, 63 et 80. L'autre page, maintenant vide, peut être supprimée, ainsi que la page racine qui, elle
aussi, est maintenant vide.
f. La page contenant la valeur 63 étant déjà à son seuil minimum, la suppression de la valeur 63 implique soit de
rééquilibrer en empruntant une valeur d'une page voisine (ayant même page parente) si une de celles-ci a assez
d'éléments, ou en la recombinant avec une page voisine sinon. En l'occurrence, la page voisine de droite est elle
aussi au seuil minimum. Il faut donc recombiner les deux pages en une en absorbant par la même occasion
l'élément (75) qui se trouvait dans la page parente, entre les deux pages à combiner. On a donc une page qui
contient les valeurs 70, 75, 76 et 79. Mais la page parente est maintenant au-dessous du seuil minimum (elle ne
contient que la valeur 80) et l'on doit donc soit emprunter une valeur à une des pages adjacentes (il n'y en a qu'une,
qui contient 15 et 40), soit combiner la page (80) avec sa voisine (15, 40) tout en absorbant l'élément de la page
au-dessus (60) qui se trouvait entre les deux pages en question. On obtient ainsi une page contenant 15, 40, 60 et
80. L'autre page, maintenant vide, peut être supprimée, ainsi que la page racine qui, elle aussi, est maintenant vide.
g. La suppression de la valeur 80 se fait en la remplaçant par la plus grande valeur du sous-arbre gauche (79) ou la
plus petite valeur du sous-arbre droit (85). Si l'on prend la valeur 79, la page qui contenait cette valeur initialement
va tomber en dessous du seuil minimum (elle ne contient plus que 76). Comme on ne peut pas emprunter une
valeur d'une page voisine, il faut combiner la page ne contenant plus que 76 avec une de ses voisines. Si l'on prend
la voisine de droite (contenant 85 et 90) et que l'on absorbe la valeur qui se trouve entre ces deux pages dans la
page parente (79), on obtient une page contenant 76, 79, 85 et 90. Mais la page parent ne contient plus que la
valeur 75 et se trouve donc en dessous du seuil minimum. Comme cette page n'a pas de voisine à laquelle
emprunter de valeur, il faut la combiner avec sa voisine (contenant 15 et 40) en absorbant la valeur de la page
parente qui se trouve entre elles (60). On obtient donc une page contenant 15, 40, 60 et 75. La page racine devient
vide et peut être supprimée.
Si l'on avait échangé la valeur 80 par la valeur 85 (plus petite valeur du sous-arbre droit), on aurait quand même
abouti au même résultat.
La première solution consiste à représenter le document XML comme un vrai arbre multiple, où la racine
de l'arbre représente l'élément principal du document XML et chaque sous-arbre représente un élément
directement imbriqué dans l'élément principal du document XML:
Cette structure d'arbre multiple peut aussi être représentée comme un arbre binaire avec un pointeur fils
pointant sur le premier sous-élément et un pointeur frère pointant d'un descendant vers un autre
descendant du même ancêtre immédiat:
display(inPair^.mCar);
write(' . ');
display(inPair^.mCdr);
write(')');
end;
end else begin
write( '()' );
end;
end;
La fermeture transitive est l'opération qui consiste à ajouter un arc entre toute paire de noeuds pour
laquelle il existe un chemin du premier noeud vers le deuxième. Il ne faut bien entendu pas oublier
d'ajouter un arc d'un noeud vers lui-même s'il existe un chemin simple qui part de ce noeud pour y
revenir (comme dans l'exemple b).
{
Parcourir et afficher a l'ecran
}
procedure parcourirBArbreEnOrdre(inArbre : PPage );
var i : integer;
begin { parcourirBArbreEnOrdre }
if (inArbre<>nil)then begin
if (inArbre^.mPremierEnfant <> nil)then begin
parcourirBArbreEnOrdre(inArbre^.mPremierEnfant);
end;
write(inArbre^.mElement[i].mCle);
write(' ');
{
Parcourirr EN ORDRE INVERSE et afficher a l'ecran
}
procedure parcourirBArbreEnOrdreInverse(inArbre : PPage );
var i : integer;
begin { parcourirBArbreEnOrdreInverse }
if (inArbre<>nil)then begin
for i:=inArbre^.mNombreEnfants to 1 do begin
write(inArbre^.mElement[i].mCle);
write(' ');
end; { for }
begin
lRoot:=nil;
lIterator:=inChaine;
while(lIterator<>nil) do begin
new(lNouveau);
lNouveau^.mContenu:=lIterator^.mContenu;
{
N.B.: Ca marche pour le premier element de la nouvelle liste aussi.
}
lNouveau^.mSuivant:=lRoot;
lRoot:=lNouveau;
lIterator:=lIterator^.mSuivant;
end; { while }
creeChaineInversee:=lRoot;
end; { creeChaineInversee }
inoutDerniereVisitee:= inRacine^.mValue;
lReturnValue:=lReturnValue and
verifieArbre(inRacine^.mRight, inoutDerniereVisitee);
verifieArbre := lReturnValue;
end; (* if else *)
end; (* verifieArbre *)
Parcours(P^.gauche);
if LastVal > P^.nombre then Ok := false
else LastVal := P^.nombre
Parcours(P^.droite);
end; (* Parcours *)
begin
Ok:= true;
LastVal:= -MaxInt;
Parcours(Racine);
return Ok;
end; (* verifieArbre *)
2.
1)
Arbre('-',
Arbre('*',
Feuille('5'),
Arbre('-',
Feuille('4'),
Arbre('+',
Feuille('3'),
Arbre('-',
nil,
Arbre('-',
nil,
Feuille('2')
)
)
)
)
),
Arbre('/',
Arbre('-',
Feuille('1'),
Feuille('9')
),
Arbre('+',
nil,
Arbre('*',
Feuille('8'),
Feuille('7')
)
)
)
)
2)
contenu du FIFO
étape L1
en notation Scheme
1 () non défini
2 ((1)) non défini
3 () (1)
4 () (1)
5 ((2 1) (4 1)) (1)
3 ((4 1)) (2 1)
4 ((4 1)) (2 1)
5 ((4 1) (5 2 1)) (2 1)
3 ((5 2 1)) (4 1)
4 ((5 2 1)) (4 1)
5 ((5 2 1) (7 4 1)) (4 1)
3 ((7 4 1)) (5 2 1)
4 ((7 4 1)) (5 2 1)
5 ((7 4 1) (6 5 2 1)) (5 2 1)
3 ((6 5 2 1)) (7 4 1)
4 ((6 5 2 1)) (7 4 1)
5 ((6 5 2 1)) (5 2 1)
3 () (6 5 2 1)
4 (TROUVE) () (6 5 2 1)
2)
function incrementeCoefficients : boolean;
begin
while ( index < LongueurMax AND Coeff[index] = MaxCoeff ) do begin
Coeff[index] := 0;
index := index + 1;
end; {while}
if ( index > LongueurMax ) then begin
incrementeCoefficients := false;
else begin
Coeff[index] := Coeff[index] + 1;
index := 0;
end; {if}
end; {function}
3)
function estParfaite : boolean;
var i : integer, code : integer;
begin
for i = 1 to MaxIndice do tab[i] := false;
for i = 1 to NbMots do begin
code := H(Vocabulaire[i]);
if ( tab[code] = true ) then begin
estParfaite := false;
exit function;
end
tab[code] := true;
end; {for}
end; {function}
4)
begin
for Taille = NbMots to TailleMax do begin
initialiseCoefficients;
while incCoeffs do
if estParfaite then exit procedure;
end; {while}
end; {for}
{ si on arrive la, aucune fonction parfaite n'a été trouvée. }
end. {procedure chercheCoefficients}
2) temp := T[hi];
T[hi] := X;
else begin
---------------------------------------------------------------------
hi := (hi+1) mod MaxElements;
if hi = H0 then begin
writeln('tableau plein');
exit(program);
end;
end;
end;
if T[Hi] = '' then
T[Hi] := X;
Insert := hi;
end;
(question)
. Si l'on adopte la convention que x->y signifie "x est plus jeune que y", on obtient le graphe
suivant:
Si l'on adopte la convention que x->y signifie "x est plus vieux/vieille que y", on obtient le graphe
suivant:
b.
J
C J a P
Arc:
A A l e c M i
ligne vers colonne
l n a J a q a P e Y
=
i d u e n u r a r v
est plus jeune que
c r d a n e i u r e
e é e n e s e l e s
Alice F F F F F F F F V V
André F F V F F F F F V F
Claude F F F F F F F F V V
Jean F F F F F F F V V F
Jeanne F F F V F F F F F F
Jacques F F F F V F F F V F
Marie F F F F F V F F F F
Paul F F F F F F F F F F
Pierre F F F F F F F V F F
Yves F F F F F F F V F F
avec cette même matrice, on pourrait considérer que les arcs vont de la colonne vers la ligne et
signifient "est plus vieux que"
c.
J
C J a P
Arc:
A A l e c M i
ligne vers colonne
l n a J a q a P e Y
=
i d u e n u r a r v
est plus jeune que
c r d a n e i u r e
e é e n e s e l e s
Alice F F F F F F F V V V
André F F V F F F F V V V
Claude F F F F F F F V V V
Jean F F F F F F F V V F
Jeanne F F F V F F F V V F
Jacques F F F V V F F V V F
Marie F F F V V V F V V F
Paul F F F F F F F F F F
Pierre F F F F F F F V F F
Yves F F F F F F F V F F
Cette fermeture transitive permet de facilement trouver qui est certainement plus jeune ou plus
vieux qu'une personne donnée. Pour trouver qui est certainement plus vieux que X, il suffit de
regarder la ligne contenant X et chercher les colonnes où il y a un "V". Pour trouver qui est
certainement plus jeune que X, il suffit de regarder la colonne contenant X et chercher les lignes
contenant un "V"
On ne peut par contre pas dire qui est lel plus vieux ou le plus jeune, car nous ne sommes pas en
présence d'une relation d'ordre totale et il n'y a peut-être aucun moyen de dire qui est le plus vieux
(la plus vielle) ou le (la) plus jeune. En l'occurrence, Alice, André et Marie sont les trois plus
jeunes, mais on ne peut rien dire sur leurs ages respectifs.
d. Marie, Alice, André, Jacques, Claude, Yves, Jeanne, Jean, Pierre, Paul. D'autres possibilités
consisteraient à permuter Alice, Marie et André (toutes les permutations sont possibles puisqu'il
n'y a aucune relation directe entre eux).
(question)
. Insertion de 1, puis de 2:
1 - 1 2
2 2 3
- -
1 3 1 3 6 1 2 6 7
Insertion de 4, puis de 5:
3 6 3 6
-
1 2 4 7 1 2 4 5 7
Insertion de 8:
3 6
1 2 4 5 7 8
Insertion de 9:
6
3 8
1 2 4 5 7 9
b. Si chaque page occupe 512 octets et qu'une page contient au minimum 1 donnée et au maximum 2
données (b-arbre d'ordre 1), le cas le plus défavorable est quand toutes les pages ne contiennent
qu'une donnée chacune et le cas le plus favorable est lorsque toutes les pages contiennent deux
données chacune. L'espace utilisé est respectivement:
❍ 10'000 données et 1 donnée/page => 10'000 pages = 10'000*512 octets
5 81 121
6 243 364
7 729 1093
8 2187 3280
9 6561 9841
Quand les pages ne sont qu'à moitié pleines, les différents niveaux contiennent:
niveau nb. pages du niveau nb. pages cumulés
1 1 1
2 2 3
3 4 7
4 8 15
5 16 31
6 32 63
7 64 127
8 128 255
9 256 511
10 512 1'023
11 1024 2'047
12 2048 4'095
13 4096 8'191
14 8192 16'383
On voit donc qu'il faut au minimum 9 niveaux pour avoir 5'000 pages pleines et au plus 14 niveaux
pour avoir 10'000 pages à moitié pleines
(question)
Pour le tableau ci-dessous, on a supposé que la position initiale d'un mot est calculée comme étant
H0(K)=(H(K) mod 15)+1
indice contenu Nb.collisions positions examinées
1 lumiere 0 1
2 soudaine 1 9, 2
3 une 0 3
4 etait 0 4
5 traversa 3 11, 4, 12, 5
6 qu 0 6
7 du 0 7
8 la 0 8
9 si 1 7, 9
10 illumination 0 10
11 jour 0 11
12 son 2 6, 9, 12
13 forte 0 13
14 esprit 0 14
15
program Lexico;
begin (* NbMots *)
Nb := 0;
if Longueur = 0 then Parcours1Niveau(Dico, -1)
else Parcours1Niveau(Dico, Longueur);
NbMots := Nb;
end; (* NbMots *)
begin
(* Dico := PseudoLecture; *)
readln(Longueur);
Dico := Lecture('990229.dat');
writeln('Nb. mots de longueur', Longueur, '=', NbMots(Longueur,Dico));
end.
const MaxNbSommets=20;
var i,
CmptrVisite,
IndexSommet : 0..MaxNbSommets;
NbSommets: integer;
begin { TriTopoInverse }
NbSommets := NombreDeSommets(LeGraphe);
CmptrVisite := 0;
for IndexSommet := 1 to NbSommets do
NumDOrdre[IndexSommet]:=0;
for IndexSommet := 1 to NbSommets do
if NumDOrdre[IndexSommet]=0 then begin
for i := 1 to NbSommets do Parcouru[i] := false;
Visite (IndexSommet);
end; { if }
end; { TriTopoInverse }
Nous allons considérer deux cas, suivant que l'on considère qu'une page ne contient que les données et
les pointeurs vers les pages descendantes ou que l'on compte, en plus, un compteur indiquant le nombre
effectif de données dans la page
❍ Quand les pages sont remplies au maximum, le nombre de niveaux est le même que pour le
B-arbre, soit 5 niveaux.
. la constrution est très similaire à celle d'un arbre binaire de tri, si ce n'est qu'à chaque fois qu'un
noeud n'a pas de descendant droit, son pointeur droit pointe vers l'ancêtre dont le contenu est
immédiatement supérieur au contenu du noeud courant, selon l'ordre de tri:
end;
end; { ConstruitArbre }
Ce qui suit est basé sur l'hypothèse que les blocs de données ne contiennent pas de référence à un bloc
de débordement
1.a)
Si les blocs de données ne contiennent pas de référence à un bloc de débordement, le plus petit
multiple de 512 qui soit aussi multiple de 80 est 2'560.
1.b)
on a 2'560 octets/bloc et 80 octets/donnée => 1 bloc=(2'560/80) données=32 données.
1.c)
round(32*80)/100) = round(25,6)=> il faudra 26 données/bloc.
1.d)
❍ Avec 26 données/bloc, 100'000 données occuperont (100'000/26) =3'847 blocs de
données
❍ une paire (clé+réf. à une page) occupe 12 octets => 1 bloc d'index de 2'560 octets peut
contenir 2'560/12 =213 paires clé+réf.
❍ la couche d'index la plus basse doit contenir 3'847/213 =19 blocs d'index
❍ la couche immédiatement supérieure contiendra 18/213 =1 bloc d'index
❍ le nombre total de blocs = 3'847 + 19 + 1 = 3'867 blocs
et comme chaque bloc occupe 2'560 octets => la place disque occupée = (3'867*2'560)
octets = 9'899'520 octets.
1.e)
l'arborescence d'index a 2 niveaux
1.f)
niveau 1: 1 bloc
niveau 2: 19 blocs
1.g)
3'847 blocs de données
Si l'on prend l'hypothèse que les blocs de données contiennent chacun une référence à un bloc de
débordement, il ne peut pas y avoir de solution à la question 1.a, c'est-à-dire qu'il n'y a pas de taille de
bloc de données qui permette de ne pas avoir d'espace disque inutilisé. Par contre, avec 19 données et des
blocs de 1'536 octets on aura le plus petit gaspillage possible (12 octets). Pour s'en convaincre, on peut
démontrer qu'il suffit d'examiner les 32 premiers multiples de 80 et calculer le nombre de pages de 512
octets qui permette de les contenir.
En effet,
si
(n*80) + 4 = m*512 + gaspillage
alors
((n+32) *80) + 4 = ((m+5)*512) + gaspillage
En d'autres termes, pour n données/bloc et (n+32) données/bloc, on a le même gaspillage (en valeur
absolue!).
1.a)
avec des blocs de 1'536 octets, on n'a que 12 octets de gaspillage
1.b)
on a 1536 octets/bloc et 80 octets/donnée => 1 bloc=(1'536-4) div 80=19 données/bloc
1.c)
round(19*80/100)=round(15,2)=15 données/bloc
1.d)
❍ avec 15 données/bloc, 100'000 données occuperont (100'000/15) =6'667 blocs de données
❍ une paire (clé+réf. à une page) occupe 12 octets => 1 bloc d'index de 1'536 octets peut
contenir 1'536/12 =128 paires clé+réf.
❍ la couche d'index la plus basse doit contenir 6'667/128 =53 blocs d'index
❍ la couche immédiatement supérieure contiendra 53/128 =1 bloc d'index
❍ le nombre total de blocs = 6'667 + 53 + 1 = 6'721 blocs
et comme chaque bloc occupe 1'536 octets => la place disque occupée = (6'721*1'536)
octets = 10'323'456 octets.
1.e)
l'arborescence d'index a 2 niveaux
1.f)
niveau 1: 1 bloc
niveau 2: 53 blocs
1.g)
6'667 blocs de données
autre solution:
autre solution:
3.a)
On peut imaginer deux genres de solution. Dans la première, on utilise un graphe dont les arcs ne sont ni orientés, ni
pondérés. Chaque noeud du graphe représente une case de la carte, et le nombre correspondant à son type de terrain est
stocké dans l'enregistrement du noeud. Il y a un arc de chaque noeud à chacun de ses voisins accessibles. Il n'est pas
nécessaire de représenter les cases impraticables dans le graphe. La représentation de la case B9 et ses voisines serait
L'autre genre de solution utilise un graphe dont les arcs sont orientés et pondérés. Un arc représente le coût d'aller d'une
case à une de ses voisines (c'est-à-dire le coût pour arriver à la case voisine). La partie du graphe autour de la case B9
serait
3.b)
Une matrice de connectivité serait la façon la plus efficace pour stocker les arcs, parce que leur densité
(NbSommets*~4/NbSommets^2 = ~0.04) dépasse 3%, en fait. Puisque le nombre de noeuds est constant, on peut
utiliser un tableau pour les stocker. Pour la première structure proposée ci-dessus, on pourrait utiliser les structures
suivantes:
const
MaxNoeuds = 100; {Pour la carte donnée}
type
NoeudIntervalle = 1..MaxNoeuds;
var
{Matrice de connectivité}
Connexe: packed array[NoeudIntervalle, NoeudIntervalle] of boolean;
{On ne stocke que le coût associé a chaque noeud}
CoutDeNoeud: array[NoeudIntervalle] of integer;
3.c)
Pour trouver le chemin le plus court, j'utiliserai la recherche en largeur, parce qu'elle évite de visiter les chemins plus
longs que le chemin recherché. Mais ce problème est différent: on ne cherche pas le chemin le plus court, on cherche le
chemin du moindre coût. On ne peut pas dire qu'on a trouvé le bon chemin dès qu'on arrive au point B - il est possible
qu'il y ait un autre chemin, peut-être plus long, mais moins coûteux.
J'utiliserais donc la recherche en profondeur avec "back-tracking" ainsi que quelques petites modifications: à tout
instant, je garde le chemin du moindre coût déjà trouvé (dans un tableau, p. ex.), ainsi que le coût lui-même. Si le coût
du chemin qu'on est en train de parcourir dépasse le coût du meilleur chemin déjà trouvé jusqu'à ce noeud là, on peut
arrêter de suivre ce chemin. Sinon, quand on arrive au noeud d'arrivée, si le coût du chemin suivi est inférieur à celui du
meilleur chemin déjà trouvé, on les échange. On continue jusqu'à ce qu'on ait essayé (peut être incomplètement) tous
les chemins possibles.
3.d)
Le programme principal:
Lire les données;
Créer le matrice de connectivité;
Obtenir les noeuds de départ et arrivée;
Initialiser les variables:
MeilleurCoût := MaxNoeuds*MaxNoeuds*MaxCoûtDeNoeud;
CheminTrouvé := Faux;
Mettre CoûtJusquIci[i] à zero pour tous les noeuds i;
TrouverCheminDuMoindreCoût(0, 1, NoeudDeDepart, NoeudDArrivee);
si CheminTrouvé alors
Ecrire MeilleurChemin et son coût
sinon
Ecrire "Pas de chemin entre NoeudDeDepart et NoeudDArrivée!"
La procédure TrouverCheminDuMoindreCoût, qui est récursive:
procedure TrouverCheminDuMoindreCoût(Coût, NombreDEtapes, Noeud, NoeudDArrivee);
begin
{Ajoute le noeud actuel au chemin qu'on est en train d'explorer}
CeChemin[NombreDEtapes] := Noeud;
inc(NombreDEtapes);
{Est-ce qu'on a trouvé le noeud d'arrivée?}
si Noeud = NoeudDArrivée
begin
CheminTrouvé := Vrai;
si Coût < MeilleurCoût
{On a trouvé un meilleur chemin, donc on le stocke}
begin
MeilleurCout := Coût;
LongueurDuChemin := NombreDEtapes - 1;
Copier CeChemin dans MeilleurChemin;
end;
end;
sinon {ce n'est pas le noeud d'arrivée}
begin
{Ajoute le coût du noeud actuel au coût du chemin qu'on explore, et le
stocke pour le noeud actuel}
Coût := Coût + CoûtDeNoeud[Noeud];
CoûtJusquIci[Noeud] := Coût;
Pour tous les noeuds i faire
si Connexe[Noeud, i] et
((CoûtJusquIci[i] = 0) ou {c'est-à-dire pas encore visité}
(Coût + CoûtDeNoeud[i] < CoûtJusquIci[i]))
{Si on a déjà trouvé ce noeud en passant par un chemin moins
coûteux, on n'étend plus ce chemin. Cette astuce réduit
beaucoup le coût de la recherche.}
begin
if (NumChaine> MaxNbSommets) OR (NumChaine < 1) then begin
writeln('L''indice du tableau doit etre entre 1 et ', MaxNbSommets);
readln;
halt
end
else begin
{Creation du noeud a inserer}
New(Entete);
with Entete^ do begin
Donnee:= Valeur;
Suivant:= nil;
end; { with }
if TabElement[NumChaine].Chaine = NIL then
TabElement[NumChaine].Chaine:= Entete
else begin
Courant:= TabElement[NumChaine].Chaine;
while Courant^.Suivant<>NIL DO Courant:= Courant^.Suivant;
{Insertion de Entete dans Courant}
Courant^.Suivant := Entete;
end; { if TabElement... else }
TabElement[NumChaine].Taillechaine :=
succ(TabElement[NumChaine].Taillechaine);
end; { if (NumChaine> MaxNbSommets)... else }
end; { Insertion }
Procedure afficher;
{Parcourir la Chaine et afficher le contenu de la Chaine}
var j: integer;
PtCourant: PtrNoeud;
begin { afficher }
for j:= 1 to MaxNumChaine do begin
writeln('TabElement[',j,']');
PtCourant:= TabElement[j].Chaine;
a)
Arbre('-',
Arbre('*',
Arbre('-',
Feuille('a'),
Arbre('+',
Feuille('b'),
Feuille('c')
)
),
Feuille('d')
),
Arbre('/',
Arbre('-',
Feuille('e'),
Feuille('f')
),
Arbre('*',
Feuille('g'),
Feuille('h')
)
)
)
b)
begin { DFS }
CmptrVisite := 0;
for IndexSommet:= 1 to NombreDeSommets(LeGraphe: Graphe) do
NumeroDOrdre[IndexSommet]:=0;
for IndexSommet := 1 to NombreDeSommets(LeGraphe: Graphe) do
if NumeroDOrdre[IndexSommet] = 0 then
Visite (IndexSommet)
end; { DFS }
begin { Imprime }
MaFile.IndexIn := 0;
MaFile.IndexOut := 0;
MaFile.Plein := false;
begin { Extension }
Sortie.NbConditions := Entree.NbConditions;
Sortie.NbRegles := Entree.NbRegles;
Sortie.NbActions := Entree.NbActions;
Regle := 1;
while Regle <= Sortie.NbRegles do begin
for Cond := 1 to Sortie.NbConditions do
if Sortie.ValCond[Regle,Cond] = Indetermine then begin
Sortie.ValCond[Regle,Cond] := Vrai;
Sortie.NbRegles := succ(Sortie.NbRegles);
for Condition := 1 to Sortie.NbConditions do
Sortie.ValCond[Sortie.NbRegles,Condition] :=
Sortie.ValCond[Regle,Condition];
Sortie.ValCond[Sortie.NbRegles,Cond] := Faux;
for Action := 1 to Sortie.NbActions do
Sortie.Agir[Sortie.NbRegles,Action] :=
Sortie.Agir[Regle,Action];
end; { if }
Regle := succ(Regle);
end; { while }
end; { Extension }
1)
2)
a) La page racine étant déjà pleine, l'adjonction de la valeur 10 va nécessiter la scission de cette page en
deux et la création d'une page au niveau au-dessus contenant l'élément charnière (qui est 10). Le B-arbre
résultant est:
b) La page dans laquelle devrait venir la nouvelle valeur (21) est déjà pleine. La page de même niveau
qui est à sa gauche peut recevoir un élément supplémentaire. L'élément intermédiaire (20) qui se trouve
dans le niveau au dessus est donc déplacé dans la page de gauche et la place ainsi libérée peut être
occupée par le plus petit élément de la page excédentaire. Le B-arbre résultant est:
c) En supprimant la valeur 55, la page en question tombe en dessous du nombre minimum d'éléments. La
page d'à côté ayant plus que le minimum d'éléments, on peut lui emprunter un élément (42) qui vient
remplacer l'élément intermédiaire (50) qui se trouve dans le niveau au dessus. Cet élément intermédiaire
(50) vient alors dans la page dans laquelle il manquait un élément. Le B-arbre résultant est:
d) La page dans laquelle devrait venir la nouvelle valeur (23) est déjà pleine. Les deux pages voisines de
même niveau sont aussi pleines. Il faut donc scinder la page excédentaire en deux, avec comme élément
charnière la valeur 23 qui ira dans la page du niveau au-dessus. Les deux pages résultant de la scission
seront connectées à l'élément charnière nouvellement rajouté au niveau au-dessus. Le B-arbre résultant
est:
e) La page dans laquelle devrait venir la nouvelle valeur (4) est déjà pleine. La page voisine de même
niveau est aussi pleine. Il faut donc scinder la page excédentaire en deux, avec comme élément charnière
la valeur 4 qui ira dans la page du niveau au-dessus. Les deux pages résultant de la scission seront
connectées à l'élément charnière nouvellement rajouté au niveau au-dessus. Le B-arbre résultant est:
f) Pour supprimer la valeur 15, on va d'abord l'échanger avec la valeur qui se trouve le plus à droite du
sous-arbre gauche, en l'occurence la valeur 7. La valeur 7 remplace donc la valeur 15 dans la page racine
et doit être supprimée de la feuille dans laquelle elle se trouvait précédemment. Cette page feuille ayant
assez d'éléments, la suppression peut se faire sans autre. Le B-arbre résultant est:
a)
Etant donné qu'un bloc de données dispose de 1536 octets et qu'une donnée occupe 76 octets, il
peut y avoir au plus 20 données par bloc (1536 div 76=20 et restent 16 octets qui peuvent être
utilisés pour pointer vers le bloc suivant et vers un eventuel bloc de débordement).
Du fait que les blocs ne sont initialement remplis qu'à 80%, il n'y aura que 16 données par bloc
(20*80/100 = 16). Il faudra donc 62'500 blocs de données pour stocker 1'000'000 éléments
(1'000'000/16 = 62'500).
Pour ce qui est des blocs d'index, chaque élément du bloc est composé d'une clé de 8 octets et
d'une référence de 4 octets, soit 12 octets par élément. Il y a donc 85 éléments par bloc d'index
(1024 div 12 = 85, reste 4).
La première couche de blocs d'index, qui pointent vers des blocs de données, doit contenir un
élément par bloc de données. Il y a donc 736 blocs (62'500 div 85 = 735, reste 25; c'est-à-dire qu'il
y a 735 blocs entièrement pleins et le dernier bloc ne contenant que 25 éléments).
La deuxième couche de blocs d'index occupe 9 blocs (736 div 85 = 8, reste 56 -> 8 blocs pleins et
le neuvième avec 56 éléments). La troisième et dernière couche de blocs d'index ne contient qu'un
seul bloc, avec seulement 9 éléments sur 85 occupés.
La taille de l'espace disque occupé est donc: (nombre de blocs de données*taille d'un bloc de
données) + (nombre de blocs d'index*taille d'un bloc d'index) = 62'500*1536 + (736+9+1)*1024 =
96'763'904 octets.
En résumé:
❍ nombre de blocs de données = 62'500
b)
Etant donné qu'une page du B-arbre dispose de 1024 octets et qu'il faut stocker une référence de
plus que de données, en supposant que l'on utilise 2 octets pour stocker le nombre d'éléments
effectifs dans la page, il peut y avoir ((1024-4-2) div (76+4) = 12, reste 58 octets inutilisés). Le
B-arbre est donc d'ordre 6.
Si l'on suppose que toutes les pages sont pleines, 1'000'000 éléments occuperaient alors 83'334
pages (1'000'000 div 12 = 83'333, reste 4 éléments qui vont occuper partiellement la 83'334ème
page).
Si, à l'autre extrème, l'on suppose que les pages ne sont qu'à moitié pleines, 1'000'000 éléments
occuperont alors 166'667 pages (1'000'000 div 6 = 166'666, reste 4 éléments qui vont occuper une
page de plus).
L'espace disque nécessaire sera donc compris entre 85'334'016 octets (83'334*1024) et
170'667'008 octets (166'667*1024).
Pour ce qui est du nombre de niveaux de l'arborescence, il faut là aussi envisager les 2 cas
❍ niveaux: entre 6 et 8.
1. Comme il s'agit d'un graphe pondéré, il faudra une matrice d'entiers pour représenter les arcs et une autre
structure pour représenter les sommets, par exemple un tableau de strings.
En supposant que les sommets sont rangés dans le tableau dans l'ordre suivant: ['Bern', 'Genève', 'Lausanne',
'Lucerne', 'Lugano', 'Milan', 'Neuchâtel', 'Zürich'], la matrice de pondération pourrait être ainsi (l'absence
d'arc est notée -1):
0 -1 96 92 -1 -1 -1 120
-1 0 60 -1 -1 325 -1 -1
96 60 0 -1 -1 -1 68 -1
92 -1 -1 0 174 -1 -1 -1
-1 -1 -1 174 0 78 -1 206
-1 325 -1 -1 78 0 -1 -1
-1 -1 68 -1 -1 -1 0 -1
120 -1 -1 -1 206 -1 -1 0
2. Les déclarations Pascal pourraient être:
begin { MinDist }
for i := 1 to NbSommets do Parcouru[i]:= false;
Index2 := index(Ville2);
DistMin := MaxInt;
Visite(index(Ville1), 0, DistMin);
MinDist := DistMin;
end; { MinDist }
Le bût du tableau Parcouru est d'éviter de boucler dans des cycles, en marquant chaque noeud par lequel
on passe. On enlève la marque dès que l'on fait marche arrière pour permettre à l'algorithme de repasser
éventuellement par ce même noeud, au cas où celui-ci se trouverait le long d'un autre chemin qui serait plus
court.
Il y a bien entendu beaucoup de solutions possibles. Le tout est de ne pas mettre un descendant avant un
de ses ancêtres. On peut donc envisager de sérialiser les données de l'arbre, une ligne après l'autre, de la
racine vers les feuilles:
a) 20 18 42 3 26 14 39 7 30 10 34
b) vos beaux yeux amour marquise d mourir font me
On aurait pu aussi imaginer faire un parcours en pré-ordre de l'arbre...
const MaxNbSommets=20;
var i,
CmptrVisite,
IndexSommet : 0..MaxNbSommets;
NbSommets: integer;
begin { TriTopoInverse }
NbSommets := NombreDeSommets(LeGraphe);
CmptrVisite := 0;
for IndexSommet := 1 to NbSommets do
NumDOrdre[IndexSommet]:=0;
for IndexSommet := 1 to NbSommets do
if NumDOrdre[IndexSommet]=0 then begin
for i := 1 to NbSommets do Parcouru[i] := false;
Visite (IndexSommet);
end; { if }
end; { TriTopoInverse }
En effet, la page où se trouvait g est tombée en dessous du seuil minimum et la page voisine est au
seuil minimum. On ne peut donc pas lui "emprunter" d'élément.
● On aboutit donc à la situation suivante, où la page qui contenait g doit être supprimée et la page du
niveau au-dessus est tombée en dessous du seuil minimum et doit fusionner avec sa voisine de
droite puisque celle-ci est au seuil minimum:
● On aboutit ainsi à la situation finale, où la page qui contenait q ainsi que la page racine doivent
être supprimées.
{ version non-recursive }
function Trouve(LeDico: VersNoeud; LeMot: string)
: boolean;
var i: integer;
Courant: VersNoeud;
begin { Trouve }
i:=1;
Courant := LeDico;
while (i<=Length(LeMot)) and (Courant<>nil) do begin
Courant := Courant^.Desc[LeMot[i]];
i := i+1;
end; { while }
if Courant=nil then Trouve := false
else Trouve := Courant^.Complet;
end; { Trouve }
begin { Imprime }
if LeDico<>nil then Print(LeDico, '');
end; { Imprime }