Adloun

Probleme – Un tableau dynamique, du contrat au coût amorti

Exercice · niveau 3 (difficile) · informatique (MP2I/MPI), chapitre 1 — Le langage C

Énoncé

On veut une structure de tableau qui grandit toute seule.

Corrigé

1. Le type.


struct tableau_s {
    int* donnees;
    int taille;       /* nombre d'éléments présents */
    int capacite;     /* nombre de cases allouées, capacite >= taille */
};
typedef struct tableau_s tableau;

L'invariant de la structure est écrit dans le commentaire : , et donnees pointe sur capacite cases valides.

2. Les opérations.


/* Renvoie un tableau vide de capacité 1, ou NULL en cas d'échec.
   L'appelant devra appeler detruire. */
tableau* creer(void) {
    tableau* t = malloc(sizeof(tableau));
    if (t == NULL) { return NULL; }
    t->donnees = malloc(1 * sizeof(int));
    if (t->donnees == NULL) { free(t); return NULL; }   /* on rend TOUT */
    t->taille = 0;
    t->capacite = 1;
    return t;
}

/* Ajoute x en fin. Renvoie true en cas de succès.
   Précondition : t non nul et bien formé. */
bool ajouter(tableau* t, int x) {
    assert(t != NULL && t->taille <= t->capacite);
    if (t->taille == t->capacite) {                     /* plein : on double */
        int neuve = 2 * t->capacite;
        int* d = malloc((size_t) neuve * sizeof(int));
        if (d == NULL) { return false; }
        for (int i = 0; i < t->taille; i = i + 1) { d[i] = t->donnees[i]; }
        free(t->donnees);
        t->donnees = d;
        t->capacite = neuve;
    }
    t->donnees[t->taille] = x;
    t->taille = t->taille + 1;
    return true;
}

/* Libère t et ses données. Précondition : t vient de creer, ou vaut NULL. */
void detruire(tableau* t) {
    if (t == NULL) { return; }
    free(t->donnees);
    free(t);
}

Remarquer la ligne if (t-&gt;donnees == NULL) { free(t); return NULL; } : en cas d'échec partiel, on rend ce qu'on a déjà pris. Sans elle, un échec d'allocation créerait une fuite.

3. Le coût amorti. Un appel isolé peut coûter : celui qui déclenche la recopie. Mais comptons le total de ajouts, en partant d'une capacité . Les recopies ont lieu quand la taille atteint avec , et la recopie de rang coûte . Le coût total des recopies vaut donc

En ajoutant les écritures elles-mêmes, le total est , soit un coût amorti de par ajout.

Pourquoi doubler, et non ajouter une constante. Si l'on agrandissait de cases à chaque fois, il y aurait recopies, coûtant : le total redeviendrait quadratique. C'est la croissance géométrique qui rend la somme télescopable, et c'est le seul point du raisonnement qui compte.

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.