Chapitre 725 minDébutant
Tableaux et listes : les algorithmes de base
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- créer un tableau, accéder à un élément et le parcourir ;
- calculer une somme, une moyenne, un maximum ou un nombre d'occurrences ;
- rechercher un élément et renvoyer sa position ;
- ajouter, insérer, supprimer et copier des éléments sans effet de bord.
7 exercices corrigés1 schéma
Un tableau est une structure qui range plusieurs valeurs sous un seul nom, chacune repérée par sa position, appelée indice ; en Python, on utilise pour cela une liste. Ce chapitre montre comment construire un tableau, accéder à ses éléments, le parcourir, et écrire les algorithmes qui reviennent dans tous les sujets : somme, moyenne, maximum, comptage, recherche, insertion, suppression et copie.
Pourquoi des tableaux ?
Pour calculer la moyenne de trois notes, trois variables suffisent. Pour trente notes, il en faudrait trente, et autant de lignes de calcul. Et si le nombre de notes change, il faut réécrire le programme. Un tableau regroupe toutes les notes dans une seule variable ; une boucle les traite toutes, quel que soit leur nombre.
| Indice | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
notes | 14 | 9 | 17 | 11 | 9 |
Le tableau notes a 5 éléments, d'indices 0 à 4. L'élément d'indice 2, noté notes[2], vaut 17. Un tableau de n éléments a des indices de 0 à n - 1 : le dernier indice n'est pas n.
Construire un tableau
En pseudo-code, on déclare le tableau avec sa taille et le type de ses éléments. Le programme parle de tableaux « de type homogène » : toutes les cases ont le même type.
Variables
notes : tableau[0..4] de réels
Début
notes[0] ← 14
notes[1] ← 9
...
En Python, un tableau à une dimension est une liste. Elle n'a pas de taille fixe et peut grandir : on peut la créer de plusieurs façons.
notes = [14, 9, 17, 11, 9] # en donnant ses valeurs
zeros = [0] * 5 # 5 cases à 0 : [0, 0, 0, 0, 0]
releves = [] # vide, puis on ajoute
releves.append(21.5)
releves.append(19.0)
print(notes, zeros, releves, len(notes))
[14, 9, 17, 11, 9] [0, 0, 0, 0, 0] [21.5, 19.0] 5
len(t) donne le nombre d'éléments. t.append(x) ajoute x à la fin : c'est la façon normale de remplir une liste dans une boucle, par exemple à partir de saisies.
n = int(input("Combien de notes ? "))
notes = []
for i in range(n):
notes.append(float(input(f"Note {i + 1} ? ")))
Accéder à un élément
On lit ou on modifie un élément par son indice, entre crochets.
notes = [14, 9, 17, 11, 9]
print(notes[0], notes[4]) # premier et dernier
notes[1] = 10 # modification
print(notes)
print(notes[-1]) # Python : le dernier élément
14 9
[14, 10, 17, 11, 9]
9
Les indices négatifs sont une facilité de Python (t[-1] est le dernier élément, t[-2] l'avant-dernier) ; ils n'existent pas en pseudo-code ni dans la plupart des langages. En algorithmique, écris t[n - 1].
Parcourir un tableau
Parcourir, c'est visiter chaque élément une fois, dans l'ordre. Deux écritures existent en Python.
Par indice, quand tu as besoin de la position (pour la renvoyer, pour comparer un élément à son voisin, pour modifier la case) :
Pour i de 0 à longueur(t) - 1 Faire
Afficher(i, " : ", t[i])
FinPour
t = [14, 9, 17]
for i in range(len(t)):
print(i, ":", t[i])
Par valeur, quand seule la valeur compte (somme, comptage) :
t = [14, 9, 17]
for note in t:
print(note)
La seconde est plus lisible et ne peut pas déborder du tableau. Quand tu as besoin des deux, enumerate donne l'indice et la valeur à chaque tour : for i, note in enumerate(t):.
| Besoin | Parcours |
|---|---|
| Somme, comptage, test d'appartenance | Par valeur |
| Renvoyer la position d'un élément | Par indice (ou enumerate) |
| Modifier les cases du tableau | Par indice : t[i] = … |
| Comparer un élément à son voisin | Par indice, de 1 à n − 1 (pour que t[i - 1] existe) |
Somme et moyenne
C'est le motif de l'accumulateur (chapitre 4) appliqué à un tableau.
Fonction Moyenne(t : tableau de réels) : réel
// Précondition : t n'est pas vide
somme ← 0
Pour i de 0 à longueur(t) - 1 Faire
somme ← somme + t[i]
FinPour
Retourner somme / longueur(t)
FinFonction
def moyenne(t):
"""Moyenne d'une liste non vide de nombres."""
somme = 0
for valeur in t:
somme = somme + valeur
return somme / len(t)
print(moyenne([14, 9, 17, 11, 9]))
12.0
Maximum et minimum
Pour trouver le plus grand élément, on garde « le plus grand vu jusqu'ici » et on le compare à chaque élément suivant.
Fonction Maximum(t : tableau de réels) : réel
// Précondition : t n'est pas vide
maxi ← t[0]
Pour i de 1 à longueur(t) - 1 Faire
Si t[i] > maxi Alors
maxi ← t[i]
FinSi
FinPour
Retourner maxi
FinFonction
def maximum(t):
maxi = t[0]
for i in range(1, len(t)):
if t[i] > maxi:
maxi = t[i]
return maxi
print(maximum([14, 9, 17, 11, 9]), maximum([-3, -8, -1]))
17 -1
Pour le minimum, on inverse la comparaison (<). Souvent, on veut la position du maximum plutôt que sa valeur, par exemple pour afficher le nom de l'étudiant qui a la meilleure note. On garde alors l'indice :
def indice_du_max(t):
i_max = 0
for i in range(1, len(t)):
if t[i] > t[i_max]:
i_max = i
return i_max
noms = ["Inès", "Hugo", "Léa", "Malik"]
notes = [14, 9, 17, 11]
i = indice_du_max(notes)
print(noms[i], "a la meilleure note :", notes[i])
Léa a la meilleure note : 17
En cas d'égalité, > garde le premier maximum rencontré ; avec >=, on garderait le dernier.
Compter
Le compteur s'incrémente quand un élément vérifie une condition.
notes = [14, 9, 17, 11, 9]
nb_sous_la_moyenne = 0
for note in notes:
if note < 10:
nb_sous_la_moyenne = nb_sous_la_moyenne + 1
print(nb_sous_la_moyenne)
2
Beaucoup d'énoncés sont des comptages déguisés : « combien de tickets sont ouverts », « y a-t-il au moins un serveur hors ligne » (compter, puis tester nb > 0), « tous les postes sont-ils à jour » (compter ceux qui ne le sont pas, puis tester nb = 0).
Rechercher un élément
La recherche séquentielle parcourt le tableau jusqu'à trouver la valeur cherchée. Elle fonctionne sur n'importe quel tableau, trié ou non. On renvoie l'indice de la première occurrence, ou −1 par convention si la valeur est absente.
En pseudo-code, une boucle TantQue exprime clairement les deux raisons de s'arrêter : on a trouvé, ou on est au bout.
Fonction Rechercher(t : tableau d'entiers, x : entier) : entier
i ← 0
trouve ← FAUX
TantQue i < longueur(t) ET NON trouve Faire
Si t[i] = x Alors
trouve ← VRAI
Sinon
i ← i + 1
FinSi
FinTantQue
Si trouve Alors
Retourner i
Sinon
Retourner -1
FinSi
FinFonction
En Python, un return dans la boucle donne une version plus courte :
def rechercher(t, x):
for i in range(len(t)):
if t[i] == x:
return i
return -1
ports = [22, 80, 443, 3306]
print(rechercher(ports, 443), rechercher(ports, 21))
2 -1
Dans le pire cas (valeur absente ou en dernière position), la recherche séquentielle examine tous les éléments. Sur un tableau trié, la recherche dichotomique fait beaucoup mieux (chapitre 10).
Ajouter, insérer, supprimer
En pseudo-code, un tableau a une taille fixe. Insérer une valeur au milieu oblige à décaler les éléments suivants d'une case vers la droite, en partant de la fin pour ne rien écraser. C'est un exercice classique, qui montre pourquoi l'insertion « coûte » plus cher qu'un ajout en fin de tableau.
// t contient n éléments utiles, dans un tableau d'au moins n + 1 cases.
// Insère x à l'indice p (0 ≤ p ≤ n).
Pour i de n à p + 1 pas -1 Faire
t[i] ← t[i - 1]
FinPour
t[p] ← x
n ← n + 1
Python fait ce travail pour toi avec ses méthodes de liste :
| Opération | Python | Effet sur t = [1, 2, 3, 4, 5] |
|---|---|---|
| Ajouter à la fin | t.append(6) | [1, 2, 3, 4, 5, 6] |
| Insérer à une position | t.insert(1, 10) | [1, 10, 2, 3, 4, 5] |
| Retirer et renvoyer le dernier | t.pop() | renvoie 5, t devient [1, 2, 3, 4] |
| Retirer à une position | t.pop(0) ou del t[0] | [2, 3, 4, 5] |
| Retirer une valeur (la première) | t.remove(3) | [1, 2, 4, 5] ; ValueError si absente |
Ces méthodes décalent les éléments en interne : insert et pop(0) sont plus lentes que append et pop() sur une très longue liste.
Copier un tableau
C'est le piège le plus surprenant de Python. L'affectation b = a ne copie pas la liste : elle donne un second nom à la même liste. Toute modification par b est visible par a.
a = [3, 8, 5]
b = a # b désigne la même liste que a
b[0] = 99
print(a)
a = [3, 8, 5]
c = a.copy() # c est une nouvelle liste, avec les mêmes valeurs
c[0] = 99
print(a, c)
[99, 8, 5]
[3, 8, 5] [99, 8, 5]
Une variable Python ne contient pas la liste elle-même, mais une référence vers elle (une sorte d'adresse). b = a copie la référence. Pour copier les valeurs, utilise a.copy(), list(a) ou a[:]. C'est le même mécanisme qui permet à une fonction de modifier une liste reçue en paramètre (chapitre 6). Le programme demande de savoir « copier un tableau » : en pseudo-code, on l'écrit avec une boucle qui recopie chaque case.
Pour i de 0 à longueur(a) - 1 Faire
copie[i] ← a[i]
FinPour
Les nombres et les chaînes, eux, ne posent pas ce problème : on ne peut pas les modifier sur place, donc partager une référence est sans danger.
Les fonctions toutes faites de Python
Python fournit la plupart de ces algorithmes en une instruction. En situation professionnelle, on s'en sert ; à l'examen, on te demande souvent l'algorithme : connais les deux.
| Besoin | Fonction Python | Algorithme à savoir écrire |
|---|---|---|
| Nombre d'éléments | len(t) | |
| Somme | sum(t) | Accumulateur |
| Maximum, minimum | max(t), min(t) | Maximum courant initialisé au premier élément |
| Appartenance | x in t | Recherche séquentielle |
| Position | t.index(x) (ValueError si absent) | Recherche séquentielle, −1 si absent |
| Nombre d'occurrences | t.count(x) | Compteur |
| Tri | sorted(t) (nouvelle liste), t.sort() (sur place) | Tris du chapitre 10 |
Python propose aussi les listes en compréhension, une écriture compacte pour construire une liste à partir d'une autre : [n * 2 for n in t] double chaque élément, [n for n in t if n >= 10] filtre. Elles sont courantes en Python professionnel ; la boucle avec append fait exactement la même chose et reste la forme à maîtriser.
Exercices
Exercice 1 sur 7 Trace d'un parcours Réussi À revoir
Fais la trace de ce programme et donne l'affichage. Que fait-il ?
t = [5, 12, 7, 3, 12]
k = 0
for i in range(1, len(t)):
if t[i] > t[k]:
k = i
print(k, t[k])
Voir la réponseMasquer la réponse de l'exercice 1
| i | t[i] | t[k] avant | t[i] > t[k] ? | k |
|---|---|---|---|---|
| avant | 0 | |||
| 1 | 12 | 5 | oui | 1 |
| 2 | 7 | 12 | non | 1 |
| 3 | 3 | 12 | non | 1 |
| 4 | 12 | 12 | non | 1 |
Affichage : 1 12. Le programme cherche l'indice du premier maximum du tableau. Le second 12, à l'indice 4, n'est pas retenu car la comparaison est stricte.
Tu avais trouvé ?
Exercice 2 sur 7 Au-dessus de la moyenne Réussi À revoir
Écris une fonction nb_au_dessus_moyenne(t) qui renvoie le nombre d'éléments strictement supérieurs à la moyenne du tableau. Pourquoi faut-il deux parcours ? Teste avec [14, 9, 17, 11, 9].
Voir la réponseMasquer la réponse de l'exercice 2
def nb_au_dessus_moyenne(t):
somme = 0
for v in t:
somme = somme + v
moy = somme / len(t)
nb = 0
for v in t:
if v > moy:
nb = nb + 1
return nb
print(nb_au_dessus_moyenne([14, 9, 17, 11, 9]))
La moyenne vaut 60 / 5 = 12 ; 14 et 17 sont au-dessus : la fonction renvoie 2. Il faut deux parcours, car on ne connaît la moyenne qu'après avoir vu tous les éléments : impossible de comparer pendant le premier.
Tu avais trouvé ?
Exercice 3 sur 7 Inverser un tableau sur place Réussi À revoir
Écris l'algorithme qui inverse l'ordre des éléments d'un tableau sans créer de second tableau : [4, 7, 1, 9, 3] devient [3, 9, 1, 7, 4]. Combien d'échanges pour un tableau de n éléments ?
Voir la réponseMasquer la réponse de l'exercice 3
On échange le premier et le dernier, puis le deuxième et l'avant-dernier, et ainsi de suite jusqu'au milieu.
n ← longueur(t)
Pour i de 0 à n DIV 2 - 1 Faire
temp ← t[i]
t[i] ← t[n - 1 - i]
t[n - 1 - i] ← temp
FinPour
t = [4, 7, 1, 9, 3]
n = len(t)
for i in range(n // 2):
t[i], t[n - 1 - i] = t[n - 1 - i], t[i]
print(t)
Il faut n DIV 2 échanges : 2 pour 5 éléments (l'élément du milieu reste en place). Si la boucle allait jusqu'au bout, chaque paire serait échangée deux fois, et le tableau reviendrait à son état initial.
Tu avais trouvé ?
Exercice 4 sur 7 Supprimer les doublons Réussi À revoir
Écris une fonction qui renvoie une nouvelle liste contenant les éléments de t sans doublon, dans l'ordre de leur première apparition. Pour [12, 5, 7, 5, 12, 3], le résultat est [12, 5, 7, 3].
Voir la réponseMasquer la réponse de l'exercice 4
def sans_doublons(t):
resultat = []
for v in t:
if v not in resultat:
resultat.append(v)
return resultat
print(sans_doublons([12, 5, 7, 5, 12, 3]))
Le test v not in resultat est lui-même une recherche séquentielle dans resultat. En pseudo-code, on l'écrirait avec la fonction Rechercher du cours : Si Rechercher(resultat, v) = -1 Alors ….
Tu avais trouvé ?
Exercice 5 sur 7 Le tableau est-il trié ? Réussi À revoir
Écris une fonction est_trie(t) qui renvoie True si le tableau est trié dans l'ordre croissant (au sens large). Donne un jeu d'essai de cinq cas, dont deux cas limites.
Voir la réponseMasquer la réponse de l'exercice 5
def est_trie(t):
for i in range(1, len(t)):
if t[i] < t[i - 1]:
return False
return True
assert est_trie([2, 5, 5, 9]) == True # cas normal, avec égalité
assert est_trie([2, 9, 5]) == False # cas normal non trié
assert est_trie([9, 2]) == False # défaut au tout début
assert est_trie([]) == True # cas limite : vide
assert est_trie([7]) == True # cas limite : un élément
print("OK")
Dès qu'une « descente » est trouvée, on renvoie False. Pour une liste vide ou d'un élément, la boucle ne fait aucun tour et la fonction renvoie True : une liste sans paire d'éléments est triée.
Tu avais trouvé ?
Exercice 6 sur 7 Copie ou pas copie ? Réussi À revoir
Qu'affiche ce programme ?
def ajouter_bonus(notes):
for i in range(len(notes)):
notes[i] = notes[i] + 1
classe = [12, 8, 15]
sauvegarde = classe
ajouter_bonus(classe)
print(classe, sauvegarde)
Voir la réponseMasquer la réponse de l'exercice 6
[13, 9, 16] [13, 9, 16]. sauvegarde = classe ne copie rien : les deux noms désignent la même liste. La fonction modifie cette liste sur place (paramètre en entrée-sortie), donc les deux noms montrent les notes augmentées. Pour une vraie sauvegarde, il fallait sauvegarde = classe.copy() avant l'appel.
Tu avais trouvé ?
Exercice 7 sur 7 Décalage circulaire Réussi À revoir
Écris l'algorithme qui décale tous les éléments d'un tableau d'une case vers la droite, le dernier revenant en première position : [1, 2, 3, 4] devient [4, 1, 2, 3]. Utilise une seule variable temporaire, et fais attention au sens du parcours.
Voir la réponseMasquer la réponse de l'exercice 7
On met le dernier élément de côté, on décale en partant de la fin (sinon chaque case écraserait la suivante avant qu'on l'ait déplacée), puis on place le dernier en tête.
t = [1, 2, 3, 4]
n = len(t)
dernier = t[n - 1]
for i in range(n - 1, 0, -1):
t[i] = t[i - 1]
t[0] = dernier
print(t)
Si l'on parcourait de gauche à droite, t[1] ← t[0] puis t[2] ← t[1] recopierait la valeur 1 partout : on obtiendrait [4, 1, 1, 1].
Tu avais trouvé ?