9782340019461_extrait
9782340019461_extrait
9782340019461_extrait
Notion de pile
1.1 Introduction
Nous utilisons en Python des objets élémentaires de type int, float, str, bool,
ou plus complexes comme les types list ou tuple. La construction et l’utilisation de
ces objets, avec des propriétés, des opérations, des méthodes, des fonctions, sont
déjà prévues par le langage.
Mais nous pouvons aussi construire et utiliser nos propres objets ou structures
de données. Cela signifie dans ce cas que nous devons implémenter de manière
explicite un ensemble d’objets avec toutes les fonctions utiles ou nécessaires, en
particulier des opérations de construction, d’accès et de modification.
Dans ce chapitre, nous allons étudier les piles, ("stack" en anglais), qui seront
construites en utilisant les objets de type list. Une liste a un début et une fin et il
est possible d’accéder à chacun de ses éléments par l’intermédiaire d’un index. Les
piles peuvent être considérées comme des listes avec des conditions d’utilisation
restreintes : il est possible d’insérer ou de supprimer un élément uniquement à la
fin, (en anglais "LIFO", acronyme de "Last In First Out" : dernier entré, premier
sorti).
En modifiant la condition d’insertion et de suppression, nous pouvons obtenir
une file ("queue" en anglais). C’est encore un objet qui peut être considéré comme
la restriction d’une liste. Une file autorise l’insertion d’un côté et la suppression
de l’autre (en anglais "FIFO", acronyme de "First In First Out" : premier entré,
premier sorti). Ceci correspond par exemple à la file d’attente à une caisse.
Il est important d’avoir toujours en tête l’image d’une pile concrète pour bien
comprendre ce qu’il est possible de faire. Si nous disposons d’assiettes que nous
pouvons manier seulement une par une, nous commençons par choisir un endroit
où les poser : c’est la création d’une pile vide. Ensuite, nous posons une première
assiette à l’endroit choisi, puis une deuxième sur la première et ainsi de suite. Nous
empilons les assiettes une par une en les posant sur la pile : ce sera le rôle de la
fonction empiler. Nous pouvons les reprendre dans l’ordre inverse en commençant
par la dernière ajoutée, toujours une par une : ce sera le rôle de la fonction dépiler.
Si les assiettes sont empilées dans un placard, la capacité est limitée par la hauteur
du placard. Si nous sommes en extérieur, il n’y a pas de limites, à part la taille
de l’échelle dont nous aurons besoin pour empiler ou dépiler et un problème de
stabilité ! Enfin, nous pouvons constater si une pile est vide ou pas et nous pouvons
compter le nombre d’assiettes empilées pour obtenir la taille de la pile.
Cette structure de pile est utilisée par la plupart des microprocesseurs : la pile
correspond à une zone de la mémoire, et le processeur retient l’adresse du dernier
élément. Nous la rencontrerons également dans le chapitre sur la récursivité.
La structure de file est elle aussi utilisée dans un ordinateur pour mémoriser
temporairement une suite d’actions en attente qui seront traitées suivant l’ordre
d’arrivée des demandes. C’est le cas, par exemple, avec les mémoires tampons
("buffers" en anglais), les serveurs d’impression et les moteurs multitâches d’un
système d’exploitation.
1.2.1 Définition
Une liste est un ensemble ordonné d’éléments éventuellement hétérogènes
dont les valeurs peuvent être modifiées.
Les éléments d’une liste sont séparés par des virgules et entourés de crochets.
liste=list(’bonjour’) # [’b’,’o’,’n’,’j’,’o’,’u’,’r’]
liste1=list(range(4)) # [0,1,2,3]
liste2=list(range(1,4)) # [1,2,3]
liste3=list(range(2,14,3)) # [2,5,8,11]
Nous pouvons construire une autre liste en itérant sur les éléments d’une liste.
liste1=[0,2,4,6,8]
liste2=list(liste1)
# ou liste2=liste1[:]
Attention : ce code peut poser problème si les éléments de la liste sont eux-
mêmes des listes.
liste1=[[0,1],[2,3],[4,5]]
liste2=list(liste1)
liste2[1][0]=8 # modifie aussi liste1
print(liste1) # affiche [[0,1],[8,3],[4,5]]
liste1=[[0,1],[2,3],[4,5]]
liste2=[list(x) for x in liste1]
Le module copy propose la fonction deepcopy pour effectuer une vraie copie
en profondeur (voir chapitre 10).
Insertion et extraction
La méthode append permet d’ajouter un élément à la fin d’une liste.
liste=[’a’,’b’]
liste.append(’c’) # (liste=[’a’,’b’,’c’])
liste=[’a’,’b’,’d’,’e’]
liste.insert(2,’c’) # l’élément ’c’ est inséré à l’index 2
print(liste) # affiche [’a’,’b’,’c’,’d’,’e’],
# ’d’ et ’e’ sont décalés vers la droite
liste=[’a’,’b’,’c’,’d’,’e’,’f’]
liste2=liste[1:4] # liste2 est la liste [’b’,’c’,’d’]
# liste[-1] est le dernier élément soit ’f’
liste=[’a’,’b’,’c’,’d’]
x=liste.pop(2) # l’élément d’indice 2 est affecté dans x
# et il est supprimé de la liste
print(liste) # affiche [’a’,’b’,’d’]
print(x) # affiche ’c’
liste=[’a’,’b’,’c’,’d’,’c’,’e’]
liste.remove(’c’) # l’élément ’c’ est supprimé
# seul le premier rencontré !
print(liste) # affiche [’a’,’b’,’d’,’c’,’e’],
# ’d’, ’c’ et ’e’ sont décalés vers la gauche
un élément renvoie une erreur) et plusieurs possibilités s’offrent à nous pour créer
et manipuler une pile de capacité c.
Voici quatre exemples de représentations d’une pile de capacité cinq contenant
trois éléments.
– Une liste de deux éléments dont le premier est la capacité et le second une
liste contenant dans l’ordre les éléments empilés : p = [5, [’A’, ’B’, ’C’]].
– Une liste dont le premier élément est la capacité, contenant ensuite dans
l’ordre les éléments empilés : p = [5, ’A’, ’B’, ’C’].
– Une liste contenant dans l’ordre les éléments empilés et dont la longueur est
la capacité : p = [’A’, ’B’, ’C’, None, None].
– une liste dont le premier élément est la taille de la pile, contenant ensuite
dans l’ordre les éléments empilés et de longueur c+1, où c est la capacité de
la pile : p = [3, ’A’, ’B’, ’C’, None, None].
Nous avons choisi pour la suite la dernière représentation mais c’est un bon
exercice d’écrire des fonctions creer_pile, empiler et depiler correspondant aux
autres représentations et voir ainsi les avantages et inconvénients de chacune.
Attention à ne pas confondre la capacité qui est le nombre d’éléments que peut
contenir une pile et la taille qui est le nombre d’éléments effectivement contenus
dans une pile.
L’un des intérêts de la représentation choisie est que p[0] est à la fois la taille
de la pile et l’index du sommet.
def depiler(p):
assert p[0]>0 # la pile n’est pas vide ?
x=p[p[0]] # le sommet de la pile
p[p[0]]=None # le sommet est retiré de la pile
p[0]=p[0]-1 # la taille diminue d’une unité
return x
Voici quelques fonctions utiles dont les définitions sont rendues très simples
grâce à la représentation utilisée :
def pile_vide(p):
return p[0]==0 # renvoie True si p[0]==0 sinon False
def taille(p):
return p[0]
def sommet(p):
assert p[0]>0
return p[p[0]]
def creer_pile():
return []
def empiler(p,x):
p.append(x)
def depiler(p):
assert len(p)>0
return p.pop()
def pile_vide(p):
return p==[]
def taille(p):
return len(p)
def sommet(p):
assert len(p)>0
return p[-1]
1.4 Applications
1.4.1 Des applications courantes
Dans un navigateur web, une pile peut servir à mémoriser les pages visitées.
L’adresse de chaque nouvelle page visitée est empilée et en cliquant sur un bouton
"Afficher la page précédente", l’utilisateur dépile l’adresse de la page précédente.
Dans un logiciel de traitement de texte, les modifications apportées au texte
sont mémorisées dans une pile, et une fonction "annuler la frappe" ("undo" en
anglais) permet de revenir en arrière pas à pas.
L’évaluation des expressions mathématiques en notation postfixée, ou notation
polonaise inverse, utilise une pile.