Logo de kxs.frCours d'informatique pour le lycée et la prépa

Dictionnaires

Spécification

Un dictionnaire ou tableau associatif est un ensemble de couple (clé, valeur). Chaque clé est unique et est associée à une valeur. Lorsqu'on ajoute un couple (clé, valeur), si la clé est déjà présente, la valeur est mise à jour, sinon le couple est simplement ajouté. Voici les primitives courantes pour une dictionnaire :

creationDictionnaire()
créer unnouveau dictionnaire ;

ajouteDico(cle, val)
ajoute le couple (cle, val) ;

retireDico(cle)
supprime le couple correpondant à cle ;

elementDico(cle)
renvoie la valeur associée à la clé ;

estPresentDico(cle)
renvoie True si la cle est dans le dictionnaire, False sinon ;

clesDico()
renvoie la liste des clés ;

valeursDico()
renvoie la liste de valeurs ;

afficheDico()
affiche les clés et les valeurs du dictionnaire ;

Comme toujours, les primitives taille() et affiche() ne sont pas toujours implémentées.

Pour simplifier l'implémentation, nous implémenterons uniquement des couples d'entiers.

Implémentation

Présentation

L'implémentation d'un dictionnaire peut se faire de nombreuses façons. Nous utiliserons une implémentation avec une liste pour les clés et une liste pour les valeurs. Un couple (cle, valeur) correspond alors aux éléments des deux listes au même indice.

Comme nous utiliserons des listes définies dans un autre fichier C, nous allons avoir besoin d'utiliser des fichiers headers.

1) Créer un fichier de headers pour le TAD liste.

#ifndef LISTES_H
#define LISTES_H

#include <stdbool.h>

// Élement de liste
typedef struct ElementListe {
	int valeur;
	struct ElementListe* suivant;
} elementListe;

// Liste qui pointe juste vers le premier élément
typedef struct ListEntier {
	int taille;
	elementListe* tete; 
} listEntier;


listEntier* creationListe() ;

listEntier* insere(listEntier* list, int pos, int val) ;

bool estVide(listEntier* list) ;

listEntier* retire(listEntier* list, int pos) ;

int taille(listEntier* list) ;

int element(listEntier* list, int pos) ;

void affiche(listEntier* list) ;

listEntier* ajouteFin(listEntier* list, int val) ;

int somme(listEntier* list) ;

listEntier* inverse(listEntier* list) ;

void remplace(listEntier* list, int pos, int val);
#endif

Et le fichier C doit contenir cela :

// Création d'une liste vide
listEntier* creationListe() {
	listEntier* liste = malloc(sizeof(listEntier));
	liste->taille = 0;
	liste->tete = NULL;
	return liste;
}

listEntier* insere(listEntier* list, int pos, int val) {
	// Si la demande est impossible
	if ((pos < 0) || (pos > list->taille)) {
		return list;
	}

	// On créer le nouvel élément
	elementListe* nouveau = malloc(sizeof(elementListe));
	nouveau->valeur = val;

	// Si on ajoute l'élément en 0
	// nouveau devient la tête et la tête devient son élément suivant
	if (pos == 0) {
		nouveau->suivant = list->tete;
		list->tete = nouveau;
	} else {
		elementListe* elt = list->tete;
		// On décale jusqu'à l'élément précédent
		for (int i = 0; i < (pos - 1); i++) {
			elt = elt->suivant;
		}
		// On insère nouveau
		nouveau->suivant = elt->suivant;
		elt->suivant = nouveau;
	}
	list->taille = list->taille + 1;
	return list;
}

bool estVide(listEntier* list) {
	if (list->taille == 0) {
		return true;
	} else {
		return false;
	}
}

listEntier* retire(listEntier* list, int pos) {
	// Si la demande n'est pas valide
	if ((pos < 0) || (pos >= list->taille)) {
		return list;
	}
	elementListe* elt;
	// si l'indice est 0, on supprime la tête
	if (pos == 0) {
		elt = list->tete->suivant;
		free(list->tete);
		list->tete = elt;
	} else {
		elt = list->tete;
		// On avance jusqu'à l'élément précédent celui qui doit être supprimé
		for (int i = 0; i < (pos - 1); i++) {
			elt = elt->suivant;
		}
		elementListe* tmp = elt->suivant;
		// On 'saute' l'élément à supprimer
		elt->suivant = elt->suivant->suivant;
		// On libère l'élément à supprimer
		free(tmp);
	}
	list->taille = list->taille - 1;
	return list;
}

int taille(listEntier* list) {
	return list->taille;
}

int element(listEntier* list, int pos) {
	elementListe* elt = list->tete;
	for (int i = 0; i < pos; i++) {
		elt = elt->suivant;
	}
	return elt->valeur;
}

void affiche(listEntier* list) {
	for (int i = 0; i < list->taille; i++) {
		printf("%d, ", element(list, i));
	}
	printf("\n");
}

Voici alors le début de l'implémentation pour le dictionnaire.

Le fichier dictionnaire.h (complet) :

#include "listes.h"
#include <stdbool.h>

#ifndef DICTIONNAIRE_H
#define DICTIONNAIRE_H

#define TAILLE_MAX 10000


typedef struct DicoEntierEntier {
	listEntier *cles;
	listEntier *valeurs;
} dicoEntierEntier;

dicoEntierEntier *creationDictionnaire();
void ajouteDico(dicoEntierEntier * dico, int cle, int val);
void retireDico(dicoEntierEntier * dico, int cle);
int elementDico(dicoEntierEntier * dico, int cle);
bool estPresentDico(dicoEntierEntier * dico, int cle);
listEntier *clesDico(dicoEntierEntier * dico);
listEntier *valeursDico(dicoEntierEntier * dico);
void afficheDico(dicoEntierEntier * dico);

#endif

Le fichier dictionnaire.c (à compléter) :

#include <stdio.h>
#include <stdlib.h>
#include "dictionnaire.h"



dicoEntierEntier *creationDictionnaire() {
	dicoEntierEntier * dico = malloc(sizeof(dicoEntierEntier));
	dico->cles = creationListe();
	dico->valeurs = creationListe();
	return dico;
}

void ajouteDico(dicoEntierEntier * dico, int cle, int val) {
	int i = 0;
	while (i < dico->cles->taille && element(dico->cles, i) != cle) {
		i++;
	}
	
	if (i == dico->cles->taille) { // On est arrivé à la fin des clés
		// On insère le couple en i
		insere(dico->cles, i, cle);
		insere(dico->valeurs, i, val);
	} else {
		// La cle existe déjà, on remplace la valeur
		remplace(dico->valeurs, i, val);
	}
}

Nous allons implémenter les autres primitives du dictionnaire. Nous aurons besoin d'ajouter une primitive à notre liste. Cette primitve est remplacer(pos, val) qui permet de remplacer une valeur à une position donnée.

2) Proposer une implémentation de cette nouvelle primitve et l'ajouter aux deux fichiers listes.c et listes.h.

void remplace(listEntier* list, int pos, int val) {
	elementListe* elt = list->tete;
	for (int i = 0; i < pos; i++) {
		elt = elt->suivant;
	}
	elt->valeur = val;
}

3) Implémenter la primitive retireDico.

4) Implémenter la primitive elementDico.

5) Implémenter la primitive estPresentDico.

6) Implémenter la primitive afficheDico.