Académique Documents
Professionnel Documents
Culture Documents
& algorithmique
Structures de données et
algorithmique
Types abstraits
Structures linéaires
Itérateurs
Exemple (suite)
Abstraction
• Lorsque l'on définit une structure de données, qu’on la programme, puis qu’on l’utilise, il est
important de connaître quel type d’opérations l'on veut exécuter sur cette structure, et à travers
ces opérations, celles que l'on veut éventuellement exécuter sur les objets que la structure de
données contient
• La manière dont les opérations sont programmées n'a pas d'intérêt pour l'utilisateur
• Un type abstrait de données (« Abstract Data Type ») est la description d'une structure de
données comprenant:
• le type (de données) des objets contenus
• les opérations possibles sur cet ensemble d’objets
• les exceptions associées à ces opérations
• Applications directes
• historique des pages visitées dans un navigateur internet
• séquences “undo” dans un éditeur de texte
• chaîne d'appels des méthodes par l’interpréteur python
• gestion de l'ensemble des processus d'un système multi-tâches (pour respecter les niveaux
de priorités et contraintes temps-réel)
• Applications indirectes
• apparaît comme structure de données auxiliaire dans certains algorithmes
• composante d'autres structures de données
class Stack():
Implémentation def __init__(self):
pass
Une pile sera définie par une classe def size() -> int:
On commence par définir une pass
interface Stack pour le type def isEmpty() -> bool:
abstrait pile pass
def top() -> object:
On devra traiter le cas d'une pile pass
vide, c'est-à-dire définir une classe def push(o):
d'exceptions pass
EmptyStackException def pop() -> object:
pass
class EmptyStackException(Exception):
def __init__(self, message):
print(message)
Exceptions
Parfois, exécuter une opération peut résulter en un état du programme que
l’on considère comme exceptionnel.
Le programmeur va traiter ces cas en particulier en utilisant un mécanisme
appelé exception
Une exception est soulevée lors de l'évaluation par le programme d’une
condition qui doit conduire à un tel traitement particulier.
Par exemple: dans le type abstrait pile, les opérations pop() et top() ne
devraient pas être exécutées si la pile est vide: dans ce cas, ces opérations
donneront lieu au traitement de l’exception EmptyStackException()
Exceptions (suite)
En général les exceptions sont définies par le programmeur: c’est un mécanisme
commode pour gérer les états peu nombreux, mais requérant des traitements
spéciaux, dans lesquels un programme peut se trouver.
Une exception est soulevée dans le programme par l’instruction
raise EmptyStackException(. . .)
Une exception est traitée dans le programme dans un bloc
try:
...
except:
...
else:
...
finally:
P …
0 1 2 t
Algorithm size()
• On insère (empile) des éléments de
gauche à droite return t + 1
• Une variable int t garde en mémoire Algorithm pop()
l'indice du sommet de la pile
if isEmpty():
raise EmptyStackException()
else:
t¬t-1
return P[t + 1]
Alain Sandoz / SA 2022 13
Structures de données
& algorithmique
…
P
0 1 2 t
Algorithm push(o)
L'opération push(o) va soulever
if t == (len(P) - 1):
une exception raise FullStackException()
FullStackException else
t¬t+1
Ceci est dû seulement à P[t] ¬ o
l'implémentation dans un tableau
Complexité et limitations
Si N est la taille du tableau utilisé dans l'implémentation
• Complexité en espace: O(N)
• Complexité en temps des opérations: O(1)
Limitations
• La longueur maximale du tableau est définie à priori et ne peut être
changée
• Essayer d'empiler un nouvel élément dans un tableau plein soulève une
exception (ceci est lié à l'implémentation)
class FullStackException(Exception):
def __init__(self, message):
print(message)
Implémentation
class ArrayStack(Stack):
pile = []
t = -1
def size(self):
return self.t + 1
def isEmpty(self):
return (self.t == -1)
def isFull(self):
return (self.size()==len(self.pile))
def push(self,o):
if self.isFull():
raise FullStackException("Pile pleine, ne peut pas empiler")
self.t+=1
self.pile[self.t]=o
def pop(self):
if self.isEmpty():
raise EmptyStackException("Pile vide, ne peut pas dépiler")
temp=self.pile[self.t]
self.pile[self.t]=None
self.t-=1
return temp
Illustration
• Lors de la construction
pile
t -1
t 3
Chaque “(”, “{”, ou “[” doit faire la paire correspondant avec “)”, “}”, ou “]”
• correct: ( )(( )){([( )])}
• correct: ((( )(( )){([( )])}))
• incorrect: )(( )){([( )])}
• incorrect: ({[ ])}
• incorrect: (
Algorithme parenthèses
Algorithm ParenMatch(X,n):
Input: An array X of n tokens, each of which is either a grouping symbol, a
variable, an arithmetic operator, or a number
Output: true if and only if all the grouping symbols in X match
Let S be an empty stack
• Une file d'attente est une structure de données qui a les opérations
new(), add(v, Q), front(Q) et remove(Q)
• Les axiomes pour ces opérations sont:
• new() retourne une file d'attente
• front(add(v, new())) = v
• remove(add(v, new())) = new()
• front(add(v, add(w, Q))) = front(add(w, Q))
• remove(add(v, add(w, Q))) = add(v, remove(add(w, Q)))
où Q est une file d'attente et v et w sont des valeurs
• L’opération remove(new()) n'est pas définie
class Queue():
Implémentation def __init__(self):
pass
Une file sera définie par une classe def size() -> int:
On commence par définir une pass
interface Queue pour le type def isEmpty() -> bool:
abstrait file pass
def front() -> object:
On devra traiter le cas d'une file pass
vide, c'est-à-dire définir une classe def enqueue(o):
d'exceptions pass
EmptyQueueException def dequeue() -> object:
pass
class EmptyQueueException(Exception):
def __init__(self, message):
print(message)
Exemples
!"#$%&'() *($&'+ ,'-+...
enqueue(5) – (5)
enqueue(3) – (5, 3)
dequeue() 5 (3)
enqueue(7) – (3, 7)
dequeue() 3 (7)
front() 7 (7)
dequeue() 7 ()
dequeue() “error” ()
isEmpty() true ()
enqueue(9) – (9)
enqueue(7) – (9, 7)
size() 2 (9, 7)
enqueue(3) – (9, 7, 3)
enqueue(5) – (9, 7, 3, 5)
dequeue() 9 (7, 3, 5)
Applications directes
• Listes/files d'attentes
• «Faire la queue» à la Poste avec son numéro de passage
• Accessibilité à des ressources partagées (imprimante, print queue)
Applications indirectes
• Apparaît comme structure de données auxiliaire dans certains algorithmes
Shared
e Service e
Algorithm isEmpty()
return (currentsize==0)
Q currentsize = 10
0 1 2 f r
Q currentsize = 10
0 1 2 r f
Q
0 1 2 f r
Q
0 1 2 r f
Alain Sandoz / SA 2022 29
Structures de données
& algorithmique
Q
0 1 2 f r
Q
0 1 2 r f
Alain Sandoz / SA 2022 30
Structures de données
& algorithmique
Complexité et limitations
Listes chaînées
• Une liste simplement chaînée est une nextNode
structure de données constituée d'une
séquence de nœuds (node)
• Chaque nœud garde en mémoire une
référence à un objet et un lien vers un autre
element node
nœud (linked list).
Remarque: ce lien est une référence vers un
autre nœud.
A B C D
Classe Node
# Modifier methods
Æ
• Détacher l'ancienne tête de la
liste
Zurich
t Æ
éléments
Classe NodeStack
class NodeStack(Stack): def push(self, elem):
v = Node(elem, self.stackTop)
stackTop=None self.stackTop = v
stackSize=0 self.stackSize+=1
f Æ
éléments
Alain Sandoz / SA 2022 40
Structures de données
& algorithmique
Problème:
insertion dans une liste / opération en fin de liste
L’endroit où insérer un élément dans une liste peut dépendre du new node
Plus généralement
Comment implémenter des opérations sur les éléments d'une structure
de données de sorte que
• l'accès aux éléments de la structure de données soit possible par l'intermédiaire de cette
implémentation
• l'accès aux éléments de la structure de données à travers sa structure d'enchaînement (organisation)
ne soit pas nécessaire
• différents usages (accès par position, recherche, parcours) soient possibles sur les instances de la
structure de données
Buts:
• garantir la protection et la consistance des contenus
• pouvoir appliquer les algorithmes indépendamment de l'implémentation des structures de données
et éventuellement les (trans)porter d’un environnement à un autre
On regroupe les opérations sur les éléments de la structure dans une classe
séparée appelée itérateur
Alain Sandoz / SA 2022 42
Structures de données
& algorithmique
def advanceCurrent(self):
def insertAfterCurrent(self, x): # raises ItemNotFoundException
def setCurrentToFirst(self):
def currentIsInList(self):
def retrieveCurrent(self):
def findAndSetCurrent(self, x):
def setCurrentToZeroth(self):
def removeAndSetCurrentToZeroth(self, x): # raises ItemNotFoundException
def removeNext(self):
current
list
def makeEmpty(self):
self.linkedListHeader.setNext(None)
class LinkedListIterator(ListIterator):
# mémorise la position courante
linkedList = None
currentPos = None
Construction
None
null p-l-1 p-l-2 p-l-3 p-l-4
linkedListHeader None
null
linkedList
currentPos
LinkedListIterator itr
Insertion
None
null p-l-1 p-l-2 p-l-3 p-l-4
linkedListHeader None
null
LinkedList myList
linkedList
itr.insertAfterCurrent(x)
itr.insertAfterCurrent( x );
currentPos
LinkedListIterator itr
Parcours
None
null p-l-1 x p-l-2 p-l-3 p-l-4
linkedListHeader None
null
linkedList
itr.advanceCurrent()
itr.advanceCurrent();
currentPos
LinkedListIterator itr
Extraction
Extraction
ListNode itr
ListNode itr interne à la méthode
interne à la méthode
while ...
while ...
None
null p-l-1 x p-l-2 p-l-3 p-l-4
linkedListHeader None
null p-l-1 x p-l-2 p-l-3 null p-l-4
None
linkedListHeader None
null
LinkedList myList éléments de la liste myList
else ...
LinkedList myList éléments de la liste myList
else ...
linkedList
linkedList itr.removeAndSetCurrentToZeroth(x) x );
itr.removeAndSetCurrentToZeroth(
currentPos
itr.removeAndSetCurrentToZeroth(x) x );
itr.removeAndSetCurrentToZeroth(
LinkedListIterator
currentPos itr
LinkedListIterator itr
Alain Sandoz / SA 2021 51
Applications
def listSize(theList):
size = 0
itr = LinkedListIterator(theList)
itr.setCurrentToFirst()
while itr.currentIsInList():
itr.advanceCurrent()
size+=1
return size
lhs.makeEmpty()
lhItr = LinkedListIterator( lhs )
rhItr = LinkedListIterator( rhs )
while rhItr.currentIsInList():
lhItr.insertAfterCurrent(rhItr.retrieveCurrent())
rhItr.advanceCurrent()
Déroulement de la copie
None
null pl-5
p-l-1 pl-6
p-l-2 pl-7
p-l-3
linkedListHeader None
None
null p-l-1 p-l-2 p-l-3 p-l-4
linkedListHeader None
null
lhs.makeEmpty()
lhs.makeEmpty();
lhItr = LinkedListIterator(lhs)
LinkedListIterator lhItr = new LinkedListIterator( lhs);
LinkedListIterator
rhItr rhItr = new LinkedListIterator( rhs);
= LinkedListIterator(rhs)
None
null pl-5
p-l-1 pl-6
p-l-2 pl-7
p-l
-3
LinkedListIterator rhItr
linkedList linkedList
currentPos currentPos
LinkedListIterator lhItr
None
null p-l-1 p-l-2 p-l-3 p-l-4
linkedListHeader None
null
None
null
linkedListHeader None
null
LinkedList lhs
LinkedListIterator rhItr
linkedList linkedList
currentPos currentPos
LinkedListIterator lhItr
rhItr.currentIsInList(
rhItr.currentIsInList() )
==== true
True
None
null p-l-1 p-l-2 p-l-3 p-l-4
linkedListHeader None
null
lhItr.insertAfterCurrent(rhItr.retrieveCurrent())
lhItr.insertAfterCurrent(
None
null rhItr.retrieveCurrent( ) );
linkedListHeader None
null
rhItr.advanceCurrent()
rhItr.advanceCurrent( );
LinkedList lhs
LinkedListIterator rhItr
linkedList linkedList
currentPos currentPos
LinkedListIterator lhItr
None
null p-l-1 p-l-2 p-l-3 p-l-4
linkedListHeader None
null
def advanceCurrent(self):
def insertAfterCurrent(self, x): # raises ItemNotFoundException
def setCurrentToFirst(self):
def currentIsInList(self):
def retrieveCurrent(self):
def findAndSetCurrent(self, x):
def setCurrentToZeroth(self):
def removeAndSetCurrentToZeroth(self, x): # raises ItemNotFoundException
def removeNext(self):
class ItemNotFoundException(Exception):
def __init__(self, message):
print(message)
def setCurrentToFirst(self):
self.currentPos = self.linkedList.linkedListHeader.getNext()
def retrieveCurrent(self):
if self.currentIsInList():
return self.currentPos.getElement()
else:
return None
def removeNext(self):
if (self.currentPos is None):
return False
if (self.currentPos.getNext() is None):
return False
self.currentPos.nextNode = self.currentPos.getNext().getNext()
return True
class MyClass(Comparable):
def __init__(self):
pass
a = MyClass()
b = MyClass()
compare = a.compares(b)
less = a.lessThan(b)
class SortListIterator(LinkedListIterator):
def insertAfterCurrent(x):
if (self.currentIsInList()):
if (self.currentPos.getElement().lessThan(x)) and
((self.currentPos.getNext() is None) or
(x.lessThan(self.currentPos.getNext().getElement()))):
newNode = Node( x, self.currentPos.getNext() )
self.currentPos.nextNode = newNode
self.currentPos = self.currentPos.getNext()
else:
l'interface Comparable
prev.insertAfterCurrent( x )
self.currentPos = prev.currentPos