Listes et algorithmes récursifs
Présentation¶
Stocker les données est un problème fondamental en informatique. Nous verrons au cours de cette année différentes manières de le faire. Étant donné une collection finie de valeurs \(a\), \(b\), \(c\), \(\ldots\), on décide de les stocker de manière séquentielle : le premier élément de la liste est \(a\), le second \(b\), etc.
On utilise pour cela la structure de liste. Si \(l\) est une liste de nombres, on dispose alors des fonctions suivantes :
| Fonction | Description |
|---|---|
creer_vide() |
Renvoie une liste vide |
est_vide(l) |
Renvoie True si et seulement si la liste est vide |
tete(l) |
Renvoie le premier élément de la liste \(l\) (l'élément dit de tête) |
queue(l) |
Renvoie la liste constituée de tous les éléments de \(l\) à l'exception du premier |
ajoute(l, e) |
Renvoie la liste constituée de l'élément \(e\), suivi des éléments de \(l\) |
affiche(l) |
Affiche la liste des éléments de \(l\) sur la sortie standard, séparés par le caractère -. |
La liste vide est représentée par x. |
On considère la liste \(l = (0, 1, 42, "Bonjour", \pi)\).
- Que renvoie
est_vide(l)? - Que renvoie
tete(l)? - Que renvoie
queue(l)? Quelle est le type dequeue(l)? - Que renvoie
ajoute(l, 93)? - Que fait l'instruction
affiche(l)?
Remarque. On peut donner une définition récursive de la structure de liste. En effet, une liste c'est : soit la liste vide ; soit un élément (la tête) suivit d'une liste (la queue).
Interface¶
Exécuter le bloc ci-dessous pour charger l'interface du type Liste.
#--- HDR ---#bksl-nlclass Liste:bksl-nl def py-undpy-undinitpy-undpy-und(self, c, n):bksl-nl assert isinstance(c, int), f"Les éléments d'une Liste sont de type int"bksl-nl assert isinstance(n, Liste) or n is None, f"La queue d'une Liste doit être une Liste"bksl-nl self.c = cbksl-nl self.n = nbksl-nlbksl-nldef creerpy-undvide() -> Liste: return Nonebksl-nldef estpy-undvide(l: Liste) -> bool: return l is Nonebksl-nldef ajoute(l: Liste, e: int) -> Liste: return Liste(e, l)bksl-nldef tete(l: Liste) -> int: return l.cbksl-nldef queue(l: Liste) -> Liste: return l.nbksl-nldef affiche(l: Liste) -> None: print("x") if estpy-undvide(l) else (print(tete(l), end = " - "), affiche(queue(l)))bksl-nldef element(l: Liste, i: int) -> int: return tete(l) if i == 0 else element(queue(l), i - 1)bksl-nl#--- HDR ---#bksl-nlbksl-nl# Exécuter ce bloc AVANT de traiter les exercices.bksl-nl# Charge les fonctions suivantes :bksl-nl# creerpy-undvide : () -> Listebksl-nl# estpy-undvide : Liste -> boolbksl-nl# ajoute : Liste, int -> Listebksl-nl# tete : Liste -> intbksl-nl# queue : Liste -> Listebksl-nl# affiche : Liste -> NoneTypebksl-nlbksl-nll = creerpy-undvide()bksl-nlaffiche(l)bksl-nll = ajoute(l, 0)bksl-nlaffiche(l)bksl-nll = ajoute(l, 1)bksl-nlajoute(l, 2)bksl-nlaffiche(l)bksl-nlbksl-nl
Commenter l'affichage réalisé.
Premier exemple¶
Écrire les instructions python permettant de stocker les listes
- \(l_1 = ()\)
- \(l_2 = (-1)\)
- \(l_3 = (5, 6, 9, -1)\)
- \(l_4 = (-5, 9, 4, 9, -5, 9)\).
Vous utiliserez uniquement les fonctions creer_vide, ajoute. Vous n'avez pas le droit d'utiliser des tableaux.
# l1 = creerpy-undvide()bksl-nl# ...bksl-nlbksl-nl
Exercices¶
Toutes les questions suivantes doivent être traitées en utilisant uniquement les fonctions décrites dans le paragraphe d'introduction. Vous n'avez pas le droit d'utiliser les tableaux. Vous n'avez pas non plus le droit d'utiliser de boucle for, ou de boucle while.
Liste singleton¶
Écrire une fonction est_singleton qui prend en argument une liste l et qui renvoie True si la liste est constituée d'un seul élément, False dans tous les autres cas.
def estpy-undsingleton(l):bksl-nl """ Liste -> boolbksl-nl Détermine si la liste est constituée d'un seul élément. """bksl-nl passbksl-nlbksl-nl
Création de liste I¶
Écrire une fonction singleton qui prend en argument un entier n et qui renvoie la liste ayant pour seul élément le nombre n.
def singleton(n):bksl-nl """ int -> Listebksl-nl Renvoie la liste (n) """bksl-nl passbksl-nlbksl-nl
Création de liste II¶
Écrire une fonction récursive nombres qui prend en argument un entier n supérieur à 1 et qui renvoie la liste des entiers de n (inclu) à 1 (inclu) dans l'ordre décroissant.
def nombres(n):bksl-nl """ int -> Listebksl-nl Renvoie la liste (n, n-1, n-2, ..., 3, 2, 1) """bksl-nl passbksl-nlbksl-nl
Création de liste III¶
On se propose dans cet exercice d'écrire une fonction nombresII qui prend en argument un entier n supérieur à 1 et qui renvoie la liste des entiers de 1 (inclus) à n (inclu) dans l'ordre croissant.
- Écrire une fonction
nombresII_auxqui étant donné deux entiersneti, renvoie la liste constituée des entiers deiàndans l'ordre croissant. - En déduire une fonction
nombresIIqui renvoie la liste dans l'ordre croissant des entiers de1àn. Vous appellerez pour cela la fonctionnombresII_auxavec des arguments bien choisis.
def nombresIIpy-undaux(n, i):bksl-nl """ int, int -> listebksl-nl Renvoie la liste de nombres (i, i + 1, ..., n-1, n) """bksl-nl passbksl-nlbksl-nldef nombresII(n):bksl-nl """ int, int -> listebksl-nl Renvoie la liste de nombres (1, 2, ..., n-1, n) """bksl-nl passbksl-nlbksl-nl
Longueur d'une liste¶
On définit la longueur d'une liste comme étant le nombre d'éléments constituant cette liste. Par exemple, si \(l = (0, 1, 5, -59)\), la longueur de \(l\) est 4. Par convention, la longueur de la liste vide est 0. Écrire une fonction python récursive longueur qui étant donné une liste, en renvoie la longueur.
def longueur(l):bksl-nl """ Liste -> intbksl-nl Renvoie la longueur de la liste l """bksl-nl passbksl-nlbksl-nl
Appartenance à une liste¶
Écrire une fonction python récursive appartient qui étant donné une liste l et un élément e, renvoie True si et seulement si l'élément e est un des éléments de la liste l.
def appartient(l, e):bksl-nl """ Liste, int -> boolbksl-nl Détermine si l'élément e fait partie de la liste l """bksl-nl passbksl-nlbksl-nl
Nombre d'occurrences¶
Écrire une fonction python récursive nombre_occurrences qui étant donné une liste l et un élément e détermine le nombre d'occurrences de l'élément e dans la liste l.
Quelle est l'avantage de l'instruction appartient(l, e) par rapport à l'instruction nombre_occurrences(l, e) == 0 pour déterminer si l'élément e appartient à la liste l ?
def nombrepy-undoccurrences(l, e):bksl-nl """ Liste, int -> intbksl-nl Compte le nombre d'occurrences de e dans l """bksl-nl passbksl-nlbksl-nl
Somme des éléments d'une liste¶
Écrire une fonction python récursive somme qui étant donnée une liste l d'entiers non vide en renvoie la somme.
def somme(l):bksl-nl """ Liste -> intbksl-nl Calcule la somme des éléments de la liste l """bksl-nl passbksl-nlbksl-nl
Maximum des éléments d'une liste¶
Écrire une fonction maximum2 qui étant donné deux entiers a et b renvoie le plus grand entier parmi a et b.
En déduire une fonction python récursive maximum qui étant donnée une liste d'entiers non vide en renvoie le plus grand.
def maximum2(a, b):bksl-nl """ int, int -> intbksl-nl Calcule l'élément maximum parmis a et bbksl-nl """bksl-nl passbksl-nlbksl-nldef maximum(l):bksl-nl """ Liste -> intbksl-nl Renvoie le plus grand élément de l """bksl-nl passbksl-nlbksl-nl
Pour aller plus loin¶
Suppression d'un élément¶
Écrire une fonction supprime qui prend en argument une liste l et un entier e, et renvoie la liste des éléments de l dans laquelle la première occurrence de e a été supprimée. On suppose que l'élément e appartient à la liste l. La liste initiale ne sera pas modifiée.
def supprime(l, e):bksl-nl """ Liste, int -> Listebksl-nl Supprime la première occurrence de e la liste l """bksl-nl passbksl-nlbksl-nl
Insérer dans une liste¶
Écrire une fonction insere qui étant donné une liste l, un élément e et un entier i renvoie la liste constituée des éléments de l dans lequel l'élément e a pour indice i. On supposera que i est un entier tel que \(0 \leq i \leq \texttt{len}(l)\). Aucun comportement particulier n'est attendu dans le cas où i ne vérifierait pas ces conditions. La liste initiale l ne sera pas modifiée.
def insere(l, e, i):bksl-nl """ Liste, int, int -> Listebksl-nl Insère l'élément e dans la liste l à l'indice i """bksl-nl passbksl-nlbksl-nl
Concaténation de deux listes¶
Si \(l_1\) et \(l_2\) sont deux listes, on appelle concaténation de \(l1\) avec \(l2\) la liste constituée des éléments de \(l_1\) puis des éléments de \(l_2\). Par exemple, la concaténation des listes \(l_1 = (0, 1, 6)\) et \(l_2 = (9, 4, 1)\) est la liste \((0, 1, 6, 9, 4, 1)\) ; par contre la concaténation des listes \(l_{2}\) et \(l_1\) (dans cet ordre) est la liste \((9, 4, 1, 0, 1, 6)\). La concaténation de la liste vide avec la liste \(l_1\) est la liste \((0, 1, 6)\).
Écrire une fonction python récursive concatene qui étant donné deux listes l1 et l2 renvoie la concaténation de ces deux listes. Les listes l1 et l2 ne seront pas modifiées.
def concatene(l1, l2):bksl-nl """ Liste, Liste -> Listebksl-nl Concatène les deux listes """bksl-nl passbksl-nlbksl-nl
Division de listes¶
- Écrire une fonction
est_2tonqui prend en argument une listelet qui renvoieTruesi et seulement si la listelest constituée d'exactement deux éléments. -
On souhaite écrire une fonction
divisequi divise en deux une listel. Les longueurs des deux listes résultantes doivent différer au plus d'un, et tous les éléments de la listeldoivent se retrouver dans l'une uniquement des deux listes de sortie. Ainsi sidivise(l), renvoie les listesl1etl2, on a :concatene(l1, l2)qui contient les mêmes éléments quel.-
lest la liste \((1, 2, 3, 4, 5)\). Parmi les propositions ci-dessous, déterminer celle qui peuvent-être renvoyées par l'instructiondivise(l):l1 = (1, 2, 3),l2 = (3, 4, 5)l1 = (1, 3, 5),l2 = (2, 4)l1 = (1, 3),l2 = (2, 4)l1 = (1, 3, 4, 5),l2 = (2)
Justifier votre réponse.
-
En déduire une fonction divise qui réponde au problème posé. Pour le cas général, on remarquera que si une liste
lest constituée d'au moins trois éléments, on peut la diviser en deux de la manière suivante :- on met à part les deux premiers éléments
x1etx2de la listel; - on divise récursivement en deux listes
l1etl2les éléments restants ; - on ajoute à
l1l'élémentx1et àl2l'élémentx2.
- on met à part les deux premiers éléments
-
def divise(l):bksl-nl """ Liste -> Liste, Listebksl-nl Divise la liste l en deux listes """bksl-nl passbksl-nlbksl-nl
Pour les plus fous¶
L'objectif ici est de déterminer l'ensemble des parties d'un ensemble \(E\) à \(n\) éléments, que l'on note \(\mathcal{P}(E)\). Par exemple, si l'ensemble \(E\) est \(\{1, 2, 3\}\), l'ensemble des parties de \(E\) est :
On remarquera plusieurs choses :
- les éléments de \(\mathcal{P}(E)\) sont des ensembles ;
- \(\emptyset \in \mathcal{P}(E)\) (on note parfois l'ensemble vide \(\{\}\) ou \(\emptyset\)) et \(E \in \mathcal{P}(E)\) ;
- Si \(E = \emptyset\), que vaut \(\mathcal{P}(E)\) ?
- Soit \(E = \{1, 2, 3\}\), et soit \(e = 3\).
- Déterminer la liste de tous les sous-ensembles de \(E\) qui ne contiennent pas \(e\). On note cette liste \(E'\).
- Déterminer la liste de tous les sous-ensembles de \(E\) qui contiennent \(e\). On note cette liste \(E''\).
- Écrire \(E\) en fonction de \(E'\) et de \(E''\). On pourra utiliser les opérations ensemblistes \(\cup\) et \(\cap\).
- Déduire des fonctions suivantes une fonction python
liste_sous_ensemblesqui étant donné un ensemble \(E\), détermine la liste de tous ses sous-ensembles. On utilisera pour cela le type pythonlist, ainsi que les méthodespop, etappend. On supposera que tous les éléments des listes manipulées sont uniques. - Si \(E\) possède \(n\) éléments, combien d'éléments possède \(\mathcal{P}(E)\) ?
def listepy-undsouspy-undensembles(E):bksl-nl """ list -> listbksl-nl Renvoie la liste de tous les sous-ensembles de E """bksl-nl passbksl-nlbksl-nl
Documents¶
- Listes et algorithmes récursifs (sujet)
- Fichiers python :