Structures linéaires : nouveau chapitre avec listes chaînées + déplacement Pile_File

This commit is contained in:
2026-06-16 09:38:57 +02:00
parent b7847e19d3
commit 5e050b384f
23 changed files with 444 additions and 1 deletions

View File

@@ -5,7 +5,8 @@
| 2 - b | Diviser pour régner : tri fusion, dichotomie récursive | Exercices diviser pour régner | | 2 - b | Diviser pour régner : tri fusion, dichotomie récursive | Exercices diviser pour régner |
| 3 - a | POO : classes, attributs, méthodes, encapsulation | TP Pokémon | | 3 - a | POO : classes, attributs, méthodes, encapsulation | TP Pokémon |
| 3 - b | Paradigmes : fonctionnel, impératif, orienté objet | TP SpotifyWrapped | | 3 - b | Paradigmes : fonctionnel, impératif, orienté objet | TP SpotifyWrapped |
| 4 | Structures linéaires : Pile, File — implémentation et usages | TP Pile, TP File, TP Navigateur | | 4 - a | Structures linéaires : Listes chaînées — maillon, insertion, suppression, récursivité | TP Playlist |
| 4 - b | Structures linéaires : Pile, File — implémentation et usages | TP Pile, TP File, TP Navigateur |
| 5 - a | Bases de données : modèle relationnel, conception, clés | TP StreamFlix | | 5 - a | Bases de données : modèle relationnel, conception, clés | TP StreamFlix |
| 5 - b | SQL : requêtes, jointures, agrégation | TP Streaming Musical | | 5 - b | SQL : requêtes, jointures, agrégation | TP Streaming Musical |
| 6 | Arbres : arbres binaires, ABR, parcours, applications | TP Livre Dont Vous Êtes le Héros, TP Recommandation | | 6 | Arbres : arbres binaires, ABR, parcours, applications | TP Livre Dont Vous Êtes le Héros, TP Recommandation |

View File

@@ -0,0 +1,101 @@
# Exercices — Listes chaînées
---
## Exercice 1 : Comprendre les maillons
On dispose du code suivant :
```python
class Maillon:
def __init__(self, valeur, suivant=None):
self.valeur = valeur
self.suivant = suivant
m3 = Maillon(30)
m2 = Maillon(20, m3)
m1 = Maillon(10, m2)
```
1. Représenter schématiquement la liste chaînée obtenue (avec les flèches).
2. Quelle est la valeur de `m1.suivant.valeur` ?
3. Quelle est la valeur de `m1.suivant.suivant.valeur` ?
4. Que vaut `m3.suivant` ?
---
## Exercice 2 : Parcourir une liste
On dispose d'une liste chaînée `l` dont la tête pointe vers `10 → 20 → 30 → 40 → None`.
```python
def afficher(liste):
courant = liste.tete
while courant is not None:
print(courant.valeur)
courant = courant.suivant
```
1. Dérouler manuellement l'exécution de `afficher(l)` : quelle est la valeur de `courant` à chaque tour de boucle ?
2. Combien de fois la boucle s'exécute-t-elle ?
3. Modifier la fonction pour qu'elle affiche les valeurs séparées par ` → ` sur une seule ligne (comme dans le cours).
---
## Exercice 3 : Opérations de base
Implémenter les méthodes suivantes dans la classe `ListeChainee` :
1. `inserer_en_queue(self, valeur)` → ajoute un maillon à la fin de la liste.
*Indication : parcourir jusqu'au dernier maillon, puis modifier son `.suivant`.*
2. `supprimer_valeur(self, valeur)` → supprime le premier maillon contenant `valeur`.
*Cas particuliers : valeur en tête, valeur absente.*
3. `nieme(self, n)` → renvoie la valeur du n-ième maillon (indice 0 = tête).
*Lever une `IndexError` si n est hors bornes.*
---
## Exercice 4 : Complexité
Pour une liste chaînée de longueur n, indiquer la complexité (O(1) ou O(n)) des opérations suivantes et justifier :
| Opération | Complexité | Justification |
|-----------|------------|---------------|
| Insérer en tête | | |
| Insérer en queue | | |
| Supprimer en tête | | |
| Accéder au n-ième élément | | |
| Rechercher une valeur | | |
---
## Exercice 5 : Version récursive
1. Écrire la fonction récursive `longueur_rec(maillon)` qui renvoie le nombre de maillons à partir de `maillon`.
2. Écrire la fonction récursive `rechercher_rec(maillon, valeur)` qui renvoie `True` si `valeur` est présente, `False` sinon.
3. Écrire la fonction récursive `renverser(maillon)` qui renvoie une nouvelle liste chaînée dont les éléments sont dans l'ordre inverse.
*Exemple : `10 → 20 → 30 → None` devient `30 → 20 → 10 → None`.*
---
## Exercice 6 : Implémenter une Pile avec une liste chaînée
En utilisant `Maillon`, implémenter la classe `Pile` avec les méthodes :
- `empiler(valeur)` → ajoute au sommet
- `depiler()` → retire et renvoie le sommet
- `sommet()` → consulte le sommet sans le retirer
- `est_vide()` → renvoie `True` si la pile est vide
Tester avec : empiler 1, 2, 3, puis dépiler jusqu'à vide.
---
Auteur : Florian Mathieu
Licence CC BY NC
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International</a>.

View File

@@ -0,0 +1,172 @@
# Listes chaînées
> Un tableau Python (`list`) permet d'accéder à n'importe quel élément en O(1) grâce aux indices — mais insérer ou supprimer un élément au milieu déplace tous ceux qui suivent. La liste chaînée adopte une autre approche : chaque élément connaît son successeur, et seuls les liens changent lors d'une modification.
## 1. Limite des tableaux pour certains usages
```python
t = [1, 2, 3, 4, 5]
t.insert(2, 99) # insère 99 à l'indice 2
# → [1, 2, 99, 3, 4, 5]
# Python a dû décaler 3, 4, 5 vers la droite : O(n)
```
Pour un tableau de 10 millions d'éléments, une insertion au début déplace 10 millions de valeurs. La liste chaînée résout ce problème : insérer en tête est toujours O(1).
---
## 2. Le maillon
L'unité de base d'une liste chaînée est un **maillon** (ou cellule, ou nœud). Il contient :
- une **valeur** (la donnée stockée)
- un **pointeur** vers le maillon suivant (ou `None` si c'est le dernier)
```python
class Maillon:
def __init__(self, valeur, suivant=None):
self.valeur = valeur
self.suivant = suivant
```
On peut enchaîner des maillons manuellement :
```python
m3 = Maillon(30)
m2 = Maillon(20, m3)
m1 = Maillon(10, m2)
# m1 → m2 → m3 → None
# 10 → 20 → 30 → None
```
---
## 3. La classe ListeChainee
On encapsule la liste dans une classe qui mémorise uniquement la **tête** (premier maillon) :
```python
class ListeChainee:
def __init__(self):
self.tete = None # liste vide
def est_vide(self):
return self.tete is None
```
### Insérer en tête
```python
def inserer_en_tete(self, valeur):
nouveau = Maillon(valeur, self.tete)
self.tete = nouveau
```
```
Avant : tete → [20] → [30] → None
Après : tete → [10] → [20] → [30] → None
```
### Supprimer en tête
```python
def supprimer_en_tete(self):
if self.est_vide():
raise IndexError("Liste vide")
valeur = self.tete.valeur
self.tete = self.tete.suivant
return valeur
```
### Afficher la liste
```python
def afficher(self):
courant = self.tete
elements = []
while courant is not None:
elements.append(str(courant.valeur))
courant = courant.suivant
print(" → ".join(elements) + " → None")
```
```python
l = ListeChainee()
l.inserer_en_tete(30)
l.inserer_en_tete(20)
l.inserer_en_tete(10)
l.afficher()
# 10 → 20 → 30 → None
```
### Longueur
```python
def longueur(self):
courant = self.tete
n = 0
while courant is not None:
n += 1
courant = courant.suivant
return n
```
### Rechercher
```python
def rechercher(self, valeur):
courant = self.tete
while courant is not None:
if courant.valeur == valeur:
return True
courant = courant.suivant
return False
```
---
## 4. Structure récursive
Une liste chaînée est naturellement **récursive** :
- soit elle est **vide**
- soit elle est un **maillon** (tête) suivi d'une **liste chaînée** (queue)
On peut écrire la longueur de manière récursive :
```python
def longueur_rec(maillon):
if maillon is None:
return 0
return 1 + longueur_rec(maillon.suivant)
```
Cette vision récursive ("tête + queue") est la même qu'on retrouve dans les arbres (chapitre Arbres).
---
## 5. Liste chaînée vs tableau Python
| | Tableau Python (`list`) | Liste chaînée |
|---|---|---|
| Accès par indice | O(1) | O(n) |
| Insertion en tête | O(n) | O(1) |
| Suppression en tête | O(n) | O(1) |
| Mémoire | Contiguë | Dispersée (maillons) |
| Usage naturel | Accès aléatoire | Insertion/suppression fréquentes |
---
## 6. Lien avec Pile et File
Une **pile** (LIFO) s'implémente naturellement avec une liste chaînée : `inserer_en_tete` = empiler, `supprimer_en_tete` = dépiler.
Une **file** (FIFO) s'implémente avec deux pointeurs : tête pour supprimer, queue pour insérer.
C'est précisément ce que vous allez implémenter dans le chapitre suivant.
---
Auteur : Florian Mathieu
Licence CC BY NC
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International</a>.

View File

@@ -0,0 +1,139 @@
# TP — Playlist musicale
> Implémenter une playlist sous forme de liste chaînée : ajouter des titres, en retirer, naviguer d'un morceau à l'autre — et comprendre pourquoi cette structure est naturellement adaptée à ce type d'usage.
---
## Contexte
Un lecteur de musique gère une **playlist** : une séquence de titres qu'on peut modifier en cours de route (ajouter un titre, supprimer un titre, passer au suivant). Un tableau Python serait peu adapté à ces insertions et suppressions fréquentes — la liste chaînée l'est beaucoup mieux.
---
## Partie 1 : La structure de base
### Étape 1 — Le maillon
Implémenter la classe `Titre` qui représente un maillon de la playlist :
```python
class Titre:
def __init__(self, nom, artiste, suivant=None):
self.nom = nom
self.artiste = artiste
self.suivant = suivant
def __str__(self):
return f"{self.nom}{self.artiste}"
```
### Étape 2 — La playlist
Implémenter la classe `Playlist` :
```python
class Playlist:
def __init__(self, nom):
self.nom = nom
self.tete = None
self.courant = None # titre en cours de lecture
```
---
## Partie 2 : Gestion de la playlist
### Question 1
Implémenter `ajouter_en_tete(self, nom, artiste)` → ajoute un titre au début de la playlist.
### Question 2
Implémenter `ajouter_en_queue(self, nom, artiste)` → ajoute un titre à la fin.
*Quelle méthode est en O(1) ? Laquelle est en O(n) ? Pourquoi ?*
### Question 3
Implémenter `supprimer(self, nom)` → supprime le premier titre dont le nom correspond.
Gérer le cas où le titre est en tête, en milieu, ou absent.
### Question 4
Implémenter `afficher(self)` → affiche tous les titres de la playlist dans l'ordre, numérotés.
```
Playlist : "Ma playlist"
1. Bohemian Rhapsody — Queen
2. Blinding Lights — The Weeknd
3. Starboy — The Weeknd
```
---
## Partie 3 : Lecture
### Question 5
Implémenter `lecture_suivante(self)` → passe au titre suivant dans la playlist.
- Si `courant` est `None`, commencer par la tête.
- Si `courant` est le dernier titre, retourner au début (liste circulaire).
### Question 6
Implémenter `lire(self)` → affiche le titre en cours de lecture.
```
♪ En cours : Bohemian Rhapsody — Queen
```
---
## Partie 4 : Fonctionnalités avancées
### Question 7
Implémenter `longueur(self)` → renvoie le nombre de titres.
### Question 8
Implémenter `rechercher(self, artiste)` → renvoie la liste de tous les titres d'un artiste donné.
### Question 9 — Défi
Implémenter `melanger(self)` → réorganise les titres dans un ordre aléatoire.
*Indication : convertir la liste chaînée en tableau Python, mélanger avec `random.shuffle`, reconstruire la liste chaînée.*
---
## Partie 5 : Test complet
```python
p = Playlist("Ma playlist")
p.ajouter_en_queue("Bohemian Rhapsody", "Queen")
p.ajouter_en_queue("Blinding Lights", "The Weeknd")
p.ajouter_en_queue("Starboy", "The Weeknd")
p.ajouter_en_tete("Hotel California", "Eagles")
p.afficher()
print(f"Longueur : {p.longueur()}")
p.lecture_suivante()
p.lire()
p.lecture_suivante()
p.lire()
p.supprimer("Starboy")
p.afficher()
print(p.rechercher("The Weeknd"))
```
---
Auteur : Florian Mathieu
Licence CC BY NC
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International</a>.

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Before

Width:  |  Height:  |  Size: 240 KiB

After

Width:  |  Height:  |  Size: 240 KiB

View File

Before

Width:  |  Height:  |  Size: 212 KiB

After

Width:  |  Height:  |  Size: 212 KiB

View File

@@ -0,0 +1,30 @@
# Structures de données linéaires
> Un tableau Python permet d'accéder instantanément à n'importe quel élément par son indice. Mais certains problèmes demandent des structures plus souples : ajouter ou supprimer des éléments sans déplacer tout le reste, ou imposer un ordre d'accès strict. C'est l'objet de ce chapitre.
## Programme
![bo.png](assets/bo.png)
## Les trois structures au programme
| Structure | Accès | Insertion | Suppression | Usage typique |
|-----------|-------|-----------|-------------|---------------|
| **Liste chaînée** | Séquentiel | En tête : O(1) | En tête : O(1) | Structure de base, implémente pile et file |
| **Pile** (stack) | Dernier entré (LIFO) | En tête : O(1) | En tête : O(1) | Appels de fonctions, annulation (Ctrl+Z) |
| **File** (queue) | Premier entré (FIFO) | En queue : O(1) | En tête : O(1) | File d'attente, BFS, impression |
## Ressources
| Fichier | Description |
|---------|-------------|
| [Listes chaînées](Listes_chainees/README.md) | La brique de base : maillon, insertion, suppression, parcours |
| [Piles et Files](Pile_File/README.md) | Deux structures aux contraintes d'accès strictes |
---
Auteur : Florian Mathieu
Licence CC BY NC
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International</a>.