Adloun

Une file avec deux piles

Exercice · niveau 2 · informatique (MP2I/MPI), chapitre 7 — Structures séquentielles : listes, piles, files

Énoncé

On ne dispose que de piles. Réaliser une file, et montrer que le coût amorti d'une opération reste .

Corrigé


/* Une file realisee par deux piles.
   INVARIANT : la file, de la tete vers la queue, est
   « sortie de son sommet vers son fond », puis « entree de son fond
   vers son sommet ». */
typedef struct { pile* entree; pile* sortie; } file2p;

void enfiler(file2p* f, int x) { pile_empiler(f->entree, x); }

int defiler(file2p* f) {
    if (pile_est_vide(f->sortie)) {              /* transfert : on RENVERSE */
        while (!pile_est_vide(f->entree)) {
            pile_empiler(f->sortie, pile_depiler(f->entree));
        }
    }
    assert(!pile_est_vide(f->sortie));           /* precondition : file non vide */
    return pile_depiler(f->sortie);
}

Vérifié : après avoir enfilé à , défilé trois fois, enfilé à puis défilé cinq fois, on obtient — l'ordre fifo.

Pourquoi cela marche. Dépiler la pile d'entrée rend ses éléments dans l'ordre inverse de leur arrivée ; les empiler sur la pile de sortie les remet donc dans l'ordre d'arrivée, et le sommet de la pile de sortie est le plus ancien. Deux renversements valent une identité, et c'est toute l'astuce.

Le coût. Un defiler isolé peut coûter : celui qui déclenche le transfert. Mais comptons le total. Chaque élément est déplacé au plus une fois de l'entrée vers la sortie — une fois transféré, il ne revient jamais. Sur une suite de enfilages et défilages, le nombre total de transferts est donc au plus , et le coût total est : le coût amorti est .

Mesuré, en comptant les transferts :


8 enfilages entrelaces de 8 defilages          :      8 transferts
100 000 enfilages puis 100 000 defilages       : 100 000 transferts
100 000 couples (enfiler, defiler) alternes    : 100 000 transferts

Dans les trois régimes, un transfert par élément : la borne est atteinte et jamais dépassée.

Le rapprochement à faire, et c'est pour cela que l'exercice figure ici : le raisonnement est exactement celui du tableau dynamique du chapitre chap:langage-c. Une opération chère, mais chaque unité de travail imputable à un élément distinct et payée une seule fois. C'est la forme la plus simple d'analyse amortie, et elle ne demande aucun outil : seulement de compter le travail total au lieu du travail d'un appel.

À quoi cela sert vraiment : à réaliser une file dans un langage ou une bibliothèque qui n'offre que des piles — et, plus utilement, à réaliser une file persistante en OCaml, où les deux piles sont deux listes immuables et où l'on obtient une file dont toutes les versions coexistent.

Les autres exercices de ce chapitre Le cours du chapitre

Un blocage sur cet exercice ? Le tuteur d'Adloun guide par questions, sans donner la réponse.