diff --git a/Algorithmique/TD/TD2.md b/Algorithmique/TD/TD2.md new file mode 100644 index 0000000000000000000000000000000000000000..68d30cb553fb2ff6fb7778e0b1dd3dcf34a5aa7e --- /dev/null +++ b/Algorithmique/TD/TD2.md @@ -0,0 +1,392 @@ +--- +title: TD2 Listes +author: VAN DE MERGHEL Robin +date: 2023 +lang: fr +geometry: margin=2cm +--- + +# Exercice 2.1 + +## Question 1 + +> **Proposer une implémentation des listes chaînées avec un tableau (le suivant dans la liste n’est pas forcément le suivant dans le tableau). Pensez à gérer la liste des cases libres.** + +| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +|---|---|---|---|---|---|---|---|---|---| +| | | | | 5 | 2 | 8 | 9 | | | + +1. Suivant dans la liste c'est le suivant dans le tableau +2. Suivant dans la liste n'est pas forcément le suivant dans le tableau + + +| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +|---|---|---|---|---|---|---|---|---|---| +| 9 | | 5 | | | 8 | | | | 5 | +| -1| | 9 | | | 2 | | | | 8 | + +Il faut gérer dans la liste l'ensemble des cases du tableau qui ne contiennent pas un élément de la liste libre : $ \{ 1, 3 , 6, 7 8 \} $ + +Ex : +1. `insererDebut(l, 20)` + +- On commence par chercher un index libre dans la liste des cases libres + + +2. Si un élément est supprimé de la liste, son index dans le tableau doit être inséré dans l'ensemble libres + +**Fonctions linéaire :** + +- `creerListe()` +- `debutListe(l)` +- `estVide(l)` +- `ajouterDebut(l, e)` +- `ajouterFin(l, e)` +- `supprimerDebut(l)` +- `supprimerFin(l)` + +3. Faire de même avec les cellules (que l'on demande au système), la liste n'est pas stockée dans un tableau. + +On implémente les deux méthodes : + +### Méthode 1 : Tableau dans un intervalle + +```ruby +function creerListe() { + n = 42 + Liste l + tab = new int[n] + l.tab = tab + l.taille = 0 + l.debut = 0 + l.fin = 0 + return l +} + +function estVide(l) { + return l.taille == 0 +} + + +function debutListe(l) { + if (estVide(l)) { + return -1 + } + return l.debut +} + +function ajouterDebut(l, e) { + if (l.taille == n) { + return -1 + } + l.tab[l.debut] = e + l.debut = (l.debut + 1) % n + l.taille = l.taille + 1 + return 0 +} + +function ajouterFin(l, e) { + if (l.taille == n) { + return -1 + } + l.tab[l.fin] = e + l.fin = (l.fin - 1) % n + l.taille = l.taille + 1 + return 0 +} + +function supprimerDebut(l) { + if (estVide(l)) { + return -1 + } + l.debut = (l.debut - 1) % n + l.taille = l.taille - 1 + return 0 +} + +function supprimerFin(l) { + if (estVide(l)) { + return -1 + } + l.fin = (l.fin + 1) % n + l.taille = l.taille - 1 + return 0 +} +``` + +### Méthode 2 : Tableau où l'on cherche les cases libres + +```ruby +function creerListe() { + n = 42 + Liste l = new Liste() + tab = new int[n] + l.tab = tab + File f = new File() + for (i = 0; i < n; i++) { + ajouterFin(f, i) + } + l.libres = f + l.taille = 0 + l.debut = 0 + l.fin = 0 + return l +} + +function estVide(l) { + return l.taille == 0 +} + +function debutListe(l) { + if (estVide(l)) { + return -1 + } + return l.debut +} + +function ajouterDebut(l, e) { + if (estVide(l.libres)) { + return -1 + } + i = supprimerDebut(l.libres) + l.tab[i] = e + l.debut = i + l.taille = l.taille + 1 + return 0 +} + +function ajouterFin(l, e) { + if (estVide(l.libres)) { + return -1 + } + i = supprimerDebut(l.libres) + l.tab[i] = e + l.fin = i + l.taille = l.taille + 1 + return 0 +} + +function supprimerDebut(l) { + if (estVide(l)) { + return -1 + } + ajouterFin(l.libres, l.debut) + l.debut = l.tab[l.debut] + l.taille = (l.taille - 1) % n + return 0 +} + +function supprimerFin(l) { + if (estVide(l)) { + return -1 + } + ajouterFin(l.libres, l.fin) + l.fin = l.tab[l.fin] + l.taille = (l.taille - 1) % n + return 0 +} +``` + + +## Question 2 + +> **Faire de même avec les pointeurs.** + +On redéfini la structure d'une cellule, on a un pointeur sur son suivant : + +```ruby +enregistrement Cellule { + T val + Cellule suiv +} +``` + +Le système a deux fonctions : + +- `allocation()` : alloue une cellule (`malloc` en C, `new` en Java) +- `liberation(c)` : libère une cellule (`free` en C, automatique en Java) + +On créera une liste avec un pointeur sur sa première cellule : + +```ruby +enregistrement Liste { + Cellule debutListe +} +``` + +Pour le code concret, on doit créer une cellule constante qui représentera la fin de la liste (comme le `null` en Java, ou le `None` en Python) : + +```ruby +Cellule cVide = ... # Dépend du langage + +# En C on peut faire : +# Cellule cVide = NULL + +# En Java on peut faire : +# Cellule cVide = null + +# En Python on peut faire : +# cVide = None +``` + +Maintenant, on peut implémenter les fonctions : + +```ruby +Liste creerListe() { + Liste l = new Liste() + l.debutListe = cVide + return l +} + +bool estVideListe(Liste l) { + return l.debutListe == cVide +} + +Cellule debutListe(Liste l) { + if (estVideListe(l)) { + return cVide + } + return l.debutListe +} + +void ajouterDebut(Liste l, T e) { + Cellule c = allocation() + + # En C on vérifie si l'allocation a réussi + # if (c == NULL) { + # printf("Erreur d'allocation\n") + # exit(1) + # } + + c.val = e + c.suiv = l.debutListe + l.debutListe = c +} + +void ajouterFin(Liste l, T e) { + Cellule c = allocation() + c.val = e + c.suiv = cVide + + if (estVideListe(l)) { + l.debutListe = c + } else { + Cellule c2 = l.debutListe + while (c2.suiv != cVide) { + c2 = c2.suiv + } + c2.suiv = c + } + +} + +Liste supprimerDebut(Liste l) { + if (estVideListe(l)) { + return l + } + Cellule c = l.debutListe + l.debutListe = c.suiv + liberation(c) + return l +} + +Liste supprimerFin(Liste l) { + if (estVideListe(l)) { + return l + } + if (l.debutListe.suiv == cVide) { + liberation(l.debutListe) + l.debutListe = cVide + return l + } + Cellule c = l.debutListe + while (c.suiv.suiv != cVide) { + c = c.suiv + } + liberation(c.suiv) + c.suiv = cVide + return l +} + +# On pourrait faire une fonction auxiliaire pour aller au dernier élément +# Et l'appeler dans ajouterFin et supprimerFin, car le code est le même +``` + +## Question 3 + +> **Comment modifier vos implémentations pour implémenter une liste chaînée bi-directionnelle ? Une liste circulaire ?** + +Pour une liste chaînée bi-directionnelle, on peut modifier l'enregistrement `Cellule` pour avoir un pointeur sur la cellule précédente : + +```ruby +enregistrement Cellule { + T val + Cellule suiv + Cellule prec +} +``` + +Un exemple de représentation : + +```mermaid +graph LR + c1 --> c2[Cellule] + c2 --> c1[Début] + c2 --> c3[Cellule] + c3 --> c2[Cellule] + c3 --> c4[Fin] + c4 --> c3[Cellule] +``` + +Pour une liste circulaire, peut modifier l'enregistrement `Liste` pour avoir un pointeur sur la dernière cellule : + +```ruby +enregistrement Liste { + Cellule debutListe + Cellule finListe +} +``` + +Un exemple de représentation : + +```mermaid +graph LR + c1 --> c2[Cellule] + c2 --> c3[Cellule] + c3 --> c4[Fin] + c4 --> c1[Début] +``` + + +# Exercice 2.3 + +## Question 1 + +> **Écrire une procédure de concaténation de deux listes en temps $O(1)$** + +On rappelle notre implémentation : on a une liste chaînée avec pointeur sur le début et la fin de la liste. + +```ruby +enregistrement Liste { + Cellule debutListe + Cellule finListe +} +``` + +On peut donc concaténer deux listes en faisant pointer la fin de la première liste sur le début de la deuxième liste : + +```ruby +Liste concatener(Liste l1, Liste l2) { + if (estVideListe(l1)) { + return l2 + } + if (estVideListe(l2)) { + return l1 + } + l1.finListe.suiv = l2.debutListe + l1.finListe = l2.finListe + return l1 +} +``` + +Le temps est bien en $O(1)$ car on ne fait que modifier les pointeurs. \ No newline at end of file diff --git a/Algorithmique/TD/TD2.pdf b/Algorithmique/TD/TD2.pdf new file mode 100644 index 0000000000000000000000000000000000000000..b3b24b1c63282c1d99a274170e5fb35db547d469 Binary files /dev/null and b/Algorithmique/TD/TD2.pdf differ