Fiche récapitulative générée pour impression / export PDF.

Licence 3 · Informatique L3 — Algorithmique, structures de données et graphes

Structures de données : piles, files, arbres et tas

Structures de données : piles, files, arbres et tas

1. Piles (LIFO) et files (FIFO)

Une pile (stack) est une structure « dernier entré, premier sorti » (LIFO) : les deux opérations de base sont empiler (push, ajouter au sommet) et dépiler (pop, retirer le sommet), chacune en O(1)O(1).

empiler(P, x) : ajouter x au sommet de P

dépiler(P) : retirer et renvoyer l'élément au sommet de P

Une file (queue) est « premier entré, premier sorti » (FIFO) : enfiler (enqueue, ajouter à l'arrière) et défiler (dequeue, retirer à l'avant), chacune en O(1)O(1) avec une implémentation adaptée (liste doublement chaînée ou tableau circulaire).

Applications classiques : une pile sert à l'évaluation d'expressions arithmétiques (notation polonaise inversée), à la gestion des appels de fonctions récursives, au parcours en profondeur (DFS) d'un graphe. Une file sert au parcours en largeur (BFS), à l'ordonnancement de tâches (FIFO).

2. Arbres binaires

Un arbre binaire est une structure récursive : chaque nœud a au plus deux enfants (gauche, droit). La hauteur hh d'un arbre à nn nœuds vérifie hlog2(n+1)1h\geq\lceil\log_2(n+1)\rceil-1 (borne atteinte par un arbre parfaitement équilibré) et hn1h\leq n-1 (cas dégénéré, arbre filiforme).

Arbre binaire de recherche (ABR) : pour chaque nœud, toutes les valeurs du sous-arbre gauche sont inférieures, toutes celles du sous-arbre droit sont supérieures. Recherche, insertion, suppression sont en O(h)O(h) — donc O(logn)O(\log n) si l'arbre est équilibré, mais O(n)O(n) dans le pire cas (arbre filiforme, par exemple après insertions dans un ordre déjà trié).

3. Arbres équilibrés

Les arbres AVL et arbres rouge-noir garantissent h=O(logn)h=O(\log n) en maintenant un équilibre lors des insertions/suppressions (rotations locales en O(1)O(1) par niveau). Cela garantit que les opérations de base restent en O(logn)O(\log n) dans le pire cas, contrairement à l'ABR naïf.

4. Tas (heap) et file de priorité

Un tas-min (min-heap) est un arbre binaire (souvent représenté par un simple tableau) vérifiant la propriété de tas : la valeur de chaque nœud est inférieure ou égale à celles de ses enfants. Conséquence : la racine contient toujours le minimum de l'ensemble.

Opérations : insérer un élément : on l'ajoute en fin de tableau puis on le fait « remonter » (percolation vers le haut) tant qu'il est plus petit que son parent — coût O(logn)O(\log n). Extraire le minimum : on retire la racine, on la remplace par le dernier élément, puis on fait « descendre » cet élément (percolation vers le bas) — coût O(logn)O(\log n) également.

Représentation par tableau : pour un nœud à l'indice ii (indexation à partir de 00), son enfant gauche est à l'indice 2i+12i+1, son enfant droit à 2i+22i+2, son parent à (i1)/2\lfloor(i-1)/2\rfloor — pas besoin de pointeurs explicites.

Application — file de priorité : un tas implémente efficacement une file de priorité (extraire systématiquement l'élément de plus haute/basse priorité), utilisée notamment dans l'algorithme de Dijkstra (leçon suivante) et le tri par tas (heapsort, O(nlogn)O(n\log n) dans tous les cas, y compris le pire).

5. Table de hachage

Une table de hachage associe à chaque clé un indice dans un tableau via une fonction de hachage hh, permettant insertion, recherche et suppression en O(1)O(1) en moyenne (sous hypothèse de bonne répartition des clés). En cas de collision (deux clés ayant le même indice), on utilise le chaînage (liste des éléments en collision à chaque case) ou l'adressage ouvert (recherche de la case libre suivante selon une règle déterministe).

Pire cas : O(n)O(n) si toutes les clés se retrouvent dans la même case (table de hachage mal conçue ou attaque délibérée), mais ce cas est rare en pratique avec une bonne fonction de hachage.

6. Récapitulatif


StructureInsertionRechercheSuppression
|---|---|---|---|




Pile / fileO(1)O(1)O(1)O(1)
ABR équilibréO(logn)O(\log n)O(logn)O(\log n)O(logn)O(\log n)
ABR non équilibré (pire cas)O(n)O(n)O(n)O(n)O(n)O(n)
Tas (min/max)O(logn)O(\log n)— (extraction min/max : O(logn)O(\log n))O(logn)O(\log n)
Table de hachage (moyenne)O(1)O(1)O(1)O(1)O(1)O(1)

Exercices de la leçon

Exercice 1

Quelle est la règle de fonctionnement d'une pile (stack) ?

Corrigé

Une pile suit la règle LIFO (Last In, First Out) : on ne peut accéder/retirer qu'au sommet, qui est le dernier élément empilé.

Exercice 2

Vrai ou faux : dans un arbre binaire de recherche (ABR), toutes les valeurs du sous-arbre gauche d'un nœud sont inférieures à la valeur de ce nœud.

Corrigé

Vrai. C'est exactement la propriété définissante d'un ABR, qui garantit que la recherche peut se faire en suivant un unique chemin de la racine à la feuille, comme une dichotomie sur un arbre.

Exercice 3

Dans un tas-min, où se trouve toujours l'élément minimal ?

Corrigé

Par la propriété de tas-min (chaque nœud \leq ses enfants), le minimum global se trouve nécessairement à la racine.

Exercice 4

Vrai ou faux : dans un tas représenté par un tableau (indexation à partir de 0), l'enfant gauche du nœud d'indice ii se trouve à l'indice 2i+12i+1.

Corrigé

Vrai. C'est la formule standard de représentation d'un tas par tableau : enfant gauche à 2i+12i+1, enfant droit à 2i+22i+2, parent à (i1)/2\lfloor(i-1)/2\rfloor.

Exercice 5

Quelle est la complexité moyenne de la recherche d'une clé dans une table de hachage bien conçue ?

Corrigé

Avec une bonne fonction de hachage répartissant uniformément les clés, le nombre moyen de collisions par case reste constant, donc la recherche se fait en temps constant en moyenne — même si le pire cas reste O(n)O(n).

Exercice 6

Un arbre binaire de recherche est construit en insérant successivement 1,2,3,4,51,2,3,4,5 (déjà triés). Quelle est sa hauteur, et pourquoi ?

Corrigé

Comme les valeurs sont insérées dans l'ordre croissant, chaque nouvelle valeur est toujours supérieure à toutes les précédentes : elle devient l'enfant droit du dernier nœud inséré. L'arbre dégénère en une « liste chaînée » de hauteur n1=4n-1=4.

Exercice 7

Pourquoi les arbres AVL ou rouge-noir garantissent-ils O(logn)O(\log n) pour les opérations de base, contrairement à un ABR naïf ?

Corrigé

Les arbres équilibrés effectuent des rotations locales après chaque insertion/suppression pour garantir que la hauteur reste O(logn)O(\log n) même dans le pire cas, contrairement à un ABR naïf qui peut dégénérer en hauteur O(n)O(n).

Exercice 8

Pour insérer un élément dans un tas-min de taille nn (le tas étant un tableau), quelle est la complexité de l'opération ?

Corrigé

On ajoute l'élément en fin de tableau (O(1)O(1)), puis on le fait remonter (percolation) le long d'un chemin de la feuille à la racine, dont la longueur est au plus la hauteur du tas, soit O(logn)O(\log n) (un tas est toujours quasi-complet, donc sa hauteur est Θ(logn)\Theta(\log n)).

Exercice 9

Vrai ou faux : une file (queue) FIFO est adaptée pour implémenter un parcours en largeur (BFS) d'un graphe.

Corrigé

Vrai. Le parcours en largeur explore les sommets niveau par niveau ; en enfilant les voisins découverts et en défilant dans l'ordre de découverte (FIFO), on garantit que tous les sommets d'un niveau sont traités avant ceux du niveau suivant.

Exercice 10

Combien de comparaisons au minimum (dans le pire cas) sont nécessaires pour trouver le minimum d'un tableau non trié de nn éléments, et pourquoi ne peut-on pas faire mieux ?

Corrigé

Borne supérieure (n1n-1 comparaisons suffisent). Un algorithme simple parcourt le tableau en gardant en mémoire le minimum courant, comparé à chaque nouvel élément : cela fait exactement n1n-1 comparaisons pour nn éléments.

Borne inférieure (n1n-1 comparaisons sont nécessaires). Argument par un graphe de « tournoi » : chaque comparaison élimine un candidat possible au rang de minimum (le plus grand des deux comparés ne peut plus être le minimum). Pour identifier avec certitude le minimum parmi nn éléments, il faut « éliminer » les n1n-1 autres candidats, chacun nécessitant d'avoir perdu au moins une comparaison. Comme chaque comparaison n'élimine qu'un seul candidat (celui qui s'avère le plus grand des deux), il faut au moins n1n-1 comparaisons.

Conclusion : n1n-1 est à la fois suffisant et nécessaire — l'algorithme naïf de parcours linéaire est donc déjà optimal pour ce problème.

Exercice 11

Démontrer que la hauteur minimale d'un arbre binaire à nn nœuds est Θ(logn)\Theta(\log n), en utilisant le fait qu'un arbre binaire de hauteur hh a au plus 2h+112^{h+1}-1 nœuds.

Corrigé

Borne sur le nombre de nœuds. Un arbre binaire de hauteur hh (la racine étant à hauteur 00) a au plus 1+2+4++2h=2h+111+2+4+\cdots+2^h=2^{h+1}-1 nœuds (un nœud à la racine, au plus 22 au niveau suivant, etc., somme géométrique).

Inversion de l'inégalité. Si l'arbre a nn nœuds, alors nécessairement n2h+11n\leq2^{h+1}-1, soit 2h+1n+12^{h+1}\geq n+1. En prenant le logarithme en base 22 :

h+1log2(n+1)    hlog2(n+1)1h+1 \geq \log_2(n+1) \implies h \geq \log_2(n+1)-1

Conclusion. La hauteur hh est donc minorée par log2(n+1)1=Θ(logn)\log_2(n+1)-1=\Theta(\log n). Cette borne est atteinte exactement par un arbre parfaitement équilibré (chaque niveau totalement rempli, sauf éventuellement le dernier) — c'est précisément ce que garantissent les arbres AVL ou rouge-noir, justifiant que leurs opérations de base sont en O(logn)O(\log n) dans le pire cas. \square

Exercice 12

Construire (par insertions successives) un tas-min à partir de la séquence 5,3,8,1,95, 3, 8, 1, 9 (insertion une à une, dans cet ordre), et donner le tableau final.

Corrigé

On insère chaque élément en fin de tableau, puis on le fait « remonter » (percoler) tant qu'il est inférieur à son parent.

Insertion de 5 : tableau [5][5].

Insertion de 3 : [5,3][5,3]. Le parent de l'indice 11 est l'indice 00 (valeur 55). Comme 3<53<5, on échange : [3,5][3,5].

Insertion de 8 : [3,5,8][3,5,8]. Le parent de l'indice 22 est l'indice 00 (valeur 33). Comme 8>38>3, pas d'échange : [3,5,8][3,5,8].

Insertion de 1 : [3,5,8,1][3,5,8,1]. Le parent de l'indice 33 est l'indice 11 (valeur 55). Comme 1<51<5, échange : [3,1,8,5][3,1,8,5]. Le parent de l'indice 11 est maintenant l'indice 00 (valeur 33). Comme 1<31<3, échange : [1,3,8,5][1,3,8,5].

Insertion de 9 : [1,3,8,5,9][1,3,8,5,9]. Le parent de l'indice 44 est l'indice 11 (valeur 33). Comme 9>39>3, pas d'échange.

Tableau final : [1,3,8,5,9][1,3,8,5,9], qui correspond bien à un tas-min valide (on vérifie : parent de l'indice 22 = indice 00 = 181\leq8 ✓ ; parent de l'indice 33 = indice 11 = 353\leq5 ✓ ; parent de l'indice 44 = indice 11 = 393\leq9 ✓).

Exercice 13

Vrai ou faux : le tri par tas (heapsort) a une complexité O(nlogn)O(n\log n) dans le pire cas, contrairement au tri rapide (quicksort) dont le pire cas est O(n2)O(n^2).

Corrigé

Vrai. Le tri par tas construit un tas en O(n)O(n), puis extrait le minimum (ou maximum) nn fois, chaque extraction coûtant O(logn)O(\log n) — soit O(nlogn)O(n\log n) dans tous les cas, sans dépendre de la configuration initiale des données, contrairement au tri rapide qui peut se dégrader en O(n2)O(n^2) sur certaines entrées défavorables.

Exercice 14

Expliquer pourquoi une table de hachage peut se dégrader à O(n)O(n) pour la recherche dans le pire cas, et quelles stratégies permettent de limiter ce risque en pratique.

Corrigé

Origine du pire cas. Une table de hachage répartit les clés dans des cases via une fonction de hachage hh. Si, pour une raison quelconque (mauvaise fonction de hachage, ou distribution adversariale des clés), un grand nombre de clés se retrouvent dans la même case, la structure de chaînage associée à cette case devient une simple liste, et la recherche d'un élément dans cette liste coûte O(k)O(k)kk est le nombre de clés en collision — dans le pire cas, k=nk=n (toutes les clés dans la même case), donnant O(n)O(n).

Stratégies de limitation en pratique :
1. Bonne fonction de hachage : choisir hh de sorte que les clés réelles (pas seulement théoriques) se répartissent le plus uniformément possible sur les cases disponibles.
2. Hachage universel (aléatoire) : tirer aléatoirement la fonction de hachage parmi une famille de fonctions au démarrage du programme, rendant impossible pour un adversaire de construire à l'avance un ensemble de clés provoquant systématiquement des collisions massives (attaque par déni de service sur les tables de hachage).
3. Redimensionnement dynamique (rehachage) : quand le facteur de charge (nombre de clés / nombre de cases) dépasse un seuil, on agrandit la table et on réinsère toutes les clés, ce qui maintient les chaînes courtes en moyenne.
4. Structures hybrides : remplacer les listes de chaînage par des arbres équilibrés au-delà d'un certain seuil de collisions dans une case (stratégie utilisée par certaines implémentations modernes de tables de hachage), garantissant O(logk)O(\log k) même dans le pire cas d'une case surchargée.

Exercice 15

Démontrer que, dans un tas-min représenté par un tableau de taille nn (indexation à partir de 00), le nombre de feuilles est exactement n/2\lceil n/2\rceil.

Corrigé

Caractérisation des feuilles. Un nœud d'indice ii (pour i{0,,n1}i\in\{0,\dots,n-1\}) est une feuille si et seulement s'il n'a pas d'enfant gauche, c'est-à-dire si l'indice de son enfant gauche, 2i+12i+1, dépasse ou égale nn :

2i+1n    in122i+1 \geq n \iff i \geq \frac{n-1}{2}

Comptage des indices vérifiant cette condition. Comme ii est un entier, la condition i(n1)/2i\geq(n-1)/2 équivaut à i(n1)/2i\geq\big\lceil(n-1)/2\big\rceil. Les indices valides sont donc i{(n1)/2,,n1}i\in\Big\{\big\lceil(n-1)/2\big\rceil,\dots,n-1\Big\}, soit un nombre d'éléments égal à :

nn12=n2+(nmod2)0  =  n2n - \Big\lceil\frac{n-1}{2}\Big\rceil = \Big\lfloor\frac{n}{2}\Big\rfloor + \big(n\bmod2\big)\cdot0 \;=\; \Big\lceil\frac{n}{2}\Big\rceil

(on vérifie directement sur des petits cas : n=5n=5 donne les indices i2i\geq2, soit i{2,3,4}i\in\{2,3,4\}, donc 3=5/23=\lceil5/2\rceil feuilles ; n=6n=6 donne i5/2=3i\geq\lceil5/2\rceil=3, soit i{3,4,5}i\in\{3,4,5\}, donc 3=6/23=\lceil6/2\rceil feuilles).

Conclusion : le nombre de feuilles d'un tas à nn nœuds est exactement n/2\big\lceil n/2\big\rceil. \square Ce résultat est notamment utilisé pour borner le coût de la construction d'un tas en O(n)O(n) (et non O(nlogn)O(n\log n) comme on pourrait naïvement le penser), car la majorité des nœuds (environ la moitié) sont des feuilles ne nécessitant aucune percolation.

AlphaMath Académie · Structures de données : piles, files, arbres et tas · Informatique L3 — Algorithmique, structures de données et graphes