xxxxxxxxxx
Pour cet exercice, on utilisera les objets `deque` du module `collections` de python qui permet d'implémenter des structures de pile et de file avec des temps d'ajout / de retrait optimisés.
On rappelle que les objets `deque` disposent - pour faire simple - de quatre méthodes :
```
- pop() pour extraire l'élément le plus à droite,
- popleft() pour extraire l'élément le plus à gauche,
- append(elt) pour ajouter un élément à droite,
- appendleft(elt) pour ajouter un élément à gauche.
```
Et qu'enfin on peut leur appliquer la méthode `len` pour savoir combien ils comportent d'éléments.
<br/>
Pour cet exercice, on utilisera les objets deque
du module collections
de python qui permet d'implémenter des structures de pile et de file avec des temps d'ajout / de retrait optimisés.
On rappelle que les objets deque
disposent - pour faire simple - de quatre méthodes :
- pop() pour extraire l'élément le plus à droite,
- popleft() pour extraire l'élément le plus à gauche,
- append(elt) pour ajouter un élément à droite,
- appendleft(elt) pour ajouter un élément à gauche.
Et qu'enfin on peut leur appliquer la méthode len
pour savoir combien ils comportent d'éléments.
xxxxxxxxxx
On considère une chaîne de caractères comportant deux formes de parenthèses :
- des parenthèses rondes `(` et `)` ,
- des parenthèses carrées `[` et `]`.
On dit que la chaîne est bien parenthésée :
- si chaque ouvrante est associée à une unique fermante de même forme et réciproquement,
- si pour chaque parenthèse ouvrante ouverte à l'intérieur d'un autre couple de parenthèses, sa parenthèse fermante se trouve également à l'intérieur du même couple.
Voici quelques exemples :
| Bien parenthésée | Mal parenthésée |
|:--------------------:|:-----------:|
| abc | ( |
|(abc)| abc)|
|ab[cd]ef |ab)c|
|a[b]c(d)e |a(b]c|
|a((b)c)d |a(b(c)d|
|a(b[c()e]f)g |a(b[c)d]e|
On considère une chaîne de caractères comportant deux formes de parenthèses :
(
et )
,[
et ]
.On dit que la chaîne est bien parenthésée :
si chaque ouvrante est associée à une unique fermante de même forme et réciproquement,
si pour chaque parenthèse ouvrante ouverte à l'intérieur d'un autre couple de parenthèses, sa parenthèse fermante se trouve également à l'intérieur du même couple.
Voici quelques exemples :
Bien parenthésée | Mal parenthésée |
---|---|
abc | ( |
(abc) | abc) |
ab[cd]ef | ab)c |
a[b]c(d)e | a(b]c |
a((b)c)d | a(b(c)d |
a(b[c()e]f)g | a(b[c)d]e |
xxxxxxxxxx
Un algorithme classique pour tester si une chaîne de caractères est bien parenthésée est le suivant qui utilise une pile :
```
Parcourir la chaîne de caractères de gauche à droite :
- Si le caractère lu est une parenthèse ouvrante l'empiler,
- Si le caractère lu est une parenthèse fermante :
- si la pile est vide : erreur de parenthésage,
- si le sommet de la pile n'est pas une ouvrante de même forme : erreur de parenthésage,
- si le sommet de la pile est une ouvrante de même forme : le dépiler,
À la fin du parcours :
- si la pile n'est pas vide : erreur de parenthésage,
- sinon : parenthésage correct.
```
Un algorithme classique pour tester si une chaîne de caractères est bien parenthésée est le suivant qui utilise une pile :
Parcourir la chaîne de caractères de gauche à droite :
- Si le caractère lu est une parenthèse ouvrante l'empiler,
- Si le caractère lu est une parenthèse fermante :
- si la pile est vide : erreur de parenthésage,
- si le sommet de la pile n'est pas une ouvrante de même forme : erreur de parenthésage,
- si le sommet de la pile est une ouvrante de même forme : le dépiler,
À la fin du parcours :
- si la pile n'est pas vide : erreur de parenthésage,
- sinon : parenthésage correct.
xxxxxxxxxx
*Question 1 :*
Pourquoi dans l'algorithme est-il important de traiter le cas où la pile est vide AVANT le cas où le sommet de la pile n'est pas une ouvrante de même forme ?
Question 1 :
Pourquoi dans l'algorithme est-il important de traiter le cas où la pile est vide AVANT le cas où le sommet de la pile n'est pas une ouvrante de même forme ?
xxxxxxxxxx
xxxxxxxxxx
<details>
<summary style="border:1pt solid slateblue; border-radius:5pt; width:15%; color:slateblue; padding:3px; background-color: lightcyan"> Solution </summary>
<div style="border:1pt solid slateblue; border-radius:5pt; color:slateblue; padding:3px; background-color: lightcyan">
Parce que pour parler de sommet de la pile, il faut s'assurer que celle-ci n'est pas vide.
Or si elle est vide, son cas a été traité avant (et met fin à l'algorithme puisqu'on sait alors que le parenthésage est incorrcet).
</div>
</details>
Parce que pour parler de sommet de la pile, il faut s'assurer que celle-ci n'est pas vide.
Or si elle est vide, son cas a été traité avant (et met fin à l'algorithme puisqu'on sait alors que le parenthésage est incorrcet).
xxxxxxxxxx
*Question 2 :*
Dans un algorithme, il y a l'instruction suivante à implémenter :
- si le sommet de la pile est différent de zéro : le dépiler et renvoyer `False`,
- si le sommet de la pile est égal à zéro : le dépiler,
Est-ce que cela correspond à la séquence d'instructions ci-dessous (où `p` est la pile) ?
```python
if p.pop() != 0:
return False
else:
p.pop()
```
Question 2 :
Dans un algorithme, il y a l'instruction suivante à implémenter :
False
,Est-ce que cela correspond à la séquence d'instructions ci-dessous (où p
est la pile) ?
if p.pop() != 0:
return False
else:
p.pop()
xxxxxxxxxx
xxxxxxxxxx
<details>
<summary style="border:1pt solid slateblue; border-radius:5pt; width:15%; color:slateblue; padding:3px; background-color: lightcyan"> Solution </summary>
<div style="border:1pt solid slateblue; border-radius:5pt; color:slateblue; padding:3px; background-color: lightcyan">
Non, car dans cette séquence d'instructions, si le sommet de la pile est égal à zéro, on effectue un `pop()` supplémentaire. Donc finalement on a retiré deux fois le sommet de la pile, ce qui revient à en retirer les deux éléments du sommet.
Il aurait fallu cette séquence d'instructions :
```python
if p.pop() != 0:
return False
else:
pass #on ne fait rien
```
</div>
</details>
Non, car dans cette séquence d'instructions, si le sommet de la pile est égal à zéro, on effectue un pop()
supplémentaire. Donc finalement on a retiré deux fois le sommet de la pile, ce qui revient à en retirer les deux éléments du sommet.
Il aurait fallu cette séquence d'instructions :
if p.pop() != 0:
return False
else:
pass #on ne fait rien
xxxxxxxxxx
*Question 3 :*
Compléter le code de la fonction `verifier_parenthesage` ci-dessous afin qu'elle mette en œuvre l'algorithme de vérification de parenthésage. Elle renverra `True` si le parentéhsage est correct et `False` sinon.
Question 3 :
Compléter le code de la fonction verifier_parenthesage
ci-dessous afin qu'elle mette en œuvre l'algorithme de vérification de parenthésage. Elle renverra True
si le parentéhsage est correct et False
sinon.
xxxxxxxxxx
from collections import deque
def verifier_parenthesage(chaine):
pile = deque()
for car in ... :
if car == '(' or car == '[':
...
elif car == ')':
if len(pile) == ... :
return ...
if ..............:
return ...
elif car == ']':
if len(pile) == ... :
return ...
if ..............:
return ...
if ......... :
.........
else:
.........
xxxxxxxxxx
assert verifier_parenthesage('(3+[5*2+3*(4-1)])*[2*(3+7)]')
assert not verifier_parenthesage('(3+[5*2+3*(4-1))])*[2*(3+7)]')
assert not verifier_parenthesage('(3+[5*2+3*(4-1*[2*(3+7')
assert not verifier_parenthesage('(3+[5*2+3*(4-1)])*[2*(3+7)]]')
xxxxxxxxxx
<details>
<summary style="border:1pt solid slateblue; border-radius:5pt; width:15%; color:slateblue; padding:3px; background-color: lightcyan"> Solution </summary>
<div style="border:1pt solid slateblue; border-radius:5pt; color:slateblue; padding:3px; background-color: lightcyan">
```python
from collections import deque
def verifier_parenthesage(chaine):
pile = deque()
for car in chaine:
if car == '(' or car == '[':
pile.append(car)
elif car == ')':
if len(pile) == 0 :
return False
if pile.pop() != '(':
return False
elif car == ']':
if len(pile) == 0:
return False
if pile.pop() != '[':
return False
if len(pile) != 0 :
return False
else:
return True
```
</div>
</details>
from collections import deque
def verifier_parenthesage(chaine):
pile = deque()
for car in chaine:
if car == '(' or car == '[':
pile.append(car)
elif car == ')':
if len(pile) == 0 :
return False
if pile.pop() != '(':
return False
elif car == ']':
if len(pile) == 0:
return False
if pile.pop() != '[':
return False
if len(pile) != 0 :
return False
else:
return True
xxxxxxxxxx
*Question 4 :*
Remplacer les quatre dernières lignes de code de la fonction par une unique ligne de code.
Question 4 :
Remplacer les quatre dernières lignes de code de la fonction par une unique ligne de code.
xxxxxxxxxx
xxxxxxxxxx
<details>
<summary style="border:1pt solid slateblue; border-radius:5pt; width:15%; color:slateblue; padding:3px; background-color: lightcyan"> Solution </summary>
<div style="border:1pt solid slateblue; border-radius:5pt; color:slateblue; padding:3px; background-color: lightcyan">
```python
return len(pile) == 0
```
</div>
</details>
return len(pile) == 0
xxxxxxxxxx
*Question 5 :*
À partir de l'indice d'une parenthèse **ouvrante** dans une chaîne de caractères **bien parenthésée**, on cherche à trouver l'indice de la parenthèse **fermante** correspondante.
L'idée est de parcourir la chaîne de caractères à partir de l'indice de la parenthèse ouvrante puis d'empiler / dépiler comme précédemment les parenthèses et crochets ouvrants et fermants **en gardant une trace de l'indice où l'on est dans la chaîne de caractères**.
Puisque l'énoncé nous indique que la chaîne est bien parenthésée, lorsqu'on tombe sur une parenthèse fermante, on peut dépiler la pile sans effectuer de test conditionnel.
On arrête le parcours dès que la pile redevient vide, on obtient alors l'indice de la fermante correspondant à l'ouvrante initiale.
Compléter le code ci-dessous qui implémente cette recherche de parenthèse fermante :
Question 5 :
À partir de l'indice d'une parenthèse ouvrante dans une chaîne de caractères bien parenthésée, on cherche à trouver l'indice de la parenthèse fermante correspondante.
L'idée est de parcourir la chaîne de caractères à partir de l'indice de la parenthèse ouvrante puis d'empiler / dépiler comme précédemment les parenthèses et crochets ouvrants et fermants en gardant une trace de l'indice où l'on est dans la chaîne de caractères.
Puisque l'énoncé nous indique que la chaîne est bien parenthésée, lorsqu'on tombe sur une parenthèse fermante, on peut dépiler la pile sans effectuer de test conditionnel.
On arrête le parcours dès que la pile redevient vide, on obtient alors l'indice de la fermante correspondant à l'ouvrante initiale.
Compléter le code ci-dessous qui implémente cette recherche de parenthèse fermante :
xxxxxxxxxx
from collections import deque
def trouver_fermante(chaine, indice_initial):
assert verifier_parenthesage(chaine), "La chaine doit etre bien parenthésée !"
pile = deque()
pile.append(chaine[indice_initial])
indice = indice_initial + 1
while len(pile) ......... :
car = chaine[indice]
if car == '(' or car == '[':
...
elif car == ')' or car == ']':
...
indice = ...
return ... #attention : ici détail technique
xxxxxxxxxx
assert trouver_fermante('(3+[5*2+3*(4-1)])*[2*(3+7)]', 0) == 16
# ^ ^
assert trouver_fermante('(3+[5*2+3*(4-1)])*[2*(3+7)]', 3) == 15
# ^ ^
assert trouver_fermante('(3+[5*2+3*(4-1)])*[2*(3+7)]', 10) == 14
# ^ ^
assert trouver_fermante('(3+[5*2+3*(4-1)])*[2*(3+7)]', 18) == 26
# ^ ^
assert trouver_fermante('(3+[5*2+3*(4-1)])*[2*(3+7)]', 21) == 25
# ^ ^
xxxxxxxxxx
<details>
<summary style="border:1pt solid slateblue; border-radius:5pt; width:15%; color:slateblue; padding:3px; background-color: lightcyan"> Solution </summary>
<div style="border:1pt solid slateblue; border-radius:5pt; color:slateblue; padding:3px; background-color: lightcyan">
```python
from collections import deque
def trouver_fermante(chaine, indice_initial):
assert verifier_parenthesage(chaine), "La chaine doit etre bien parenthésée !"
pile = deque()
pile.append(chaine[indice_initial])
indice = indice_initial + 1
while len(pile) != 0 :
car = chaine[indice]
if car == '(' or car == '[':
pile.append(car)
elif car == ')' or car == ']':
pile.pop()
indice = indice + 1
return indice - 1
```
</div>
</details>
from collections import deque
def trouver_fermante(chaine, indice_initial):
assert verifier_parenthesage(chaine), "La chaine doit etre bien parenthésée !"
pile = deque()
pile.append(chaine[indice_initial])
indice = indice_initial + 1
while len(pile) != 0 :
car = chaine[indice]
if car == '(' or car == '[':
pile.append(car)
elif car == ')' or car == ']':
pile.pop()
indice = indice + 1
return indice - 1