diff --git a/slides/cours_11.md b/slides/cours_11.md index b8a0c54fc7f95a8ea76ae4a4803ae8f724bc5207..ec097a1affb1b62b3cd295428258d0d9399cc647 100644 --- a/slides/cours_11.md +++ b/slides/cours_11.md @@ -425,516 +425,3 @@ void stack_destroy(stack *s) { } ``` -# La file d'attente - -\Huge La file d'attente - -# La file d'attente (1/2) - -* Structure de données abstraite permettant le stockage d'éléments. -* *FIFO*: First In First Out, ou première entrée première sortie. -* Analogue de la vie "réelle"": - * File à un guichet, - * Serveur d'impressions, - * Mémoire tampon, ... - -## Fonctionnalités - - . . . - -* Enfiler: ajouter un élément à la fin de la file. -* Défiler: extraire un élément au devant de la file. -* Tester si la file est vide. - -. . . - -* Lire l'élément de la fin de la file. -* Lire l'élément du devant de la file. -* Créer une file vide. -* Détruire une file. - -# La file d'attente (2/2) - -\footnotesize - -## Implémentation possible - -* La structure de file, contient un pointeur vers la tête et un autre vers le début de la file. -* Entre les deux, les éléments sont stockés dans une liste chaînée. - -{width=80%} - -## Structure de données en C? - -. . . - -```C -typedef struct _element { // Elément de liste - int data; - struct _element* next; -} element; -typedef struct _queue { // File d'attente - element* head; // tête de file d'attente - element* tail; // queue de file d'attente -} queue; -``` - -# Fonctionnalités d'une file d'attente - -## Création et consultations - -. . . - -```C -void queue_init(queue *fa); // head = tail = NULL -bool queue_is_empty(queue fa); // fa.head == fa.tail == NULL -int queue_tail(queue fa); // return fa.tail->data -int queue_head(queue fa); // return fa.head->data -``` - -## Manipulations et destruction - -. . . - -```C -void queue_enqueue(queue *fa, int val); -// adds an element before the tail -int queue_dequeue(queue *fa); -// removes the head and returns stored value -void queue_destroy(queue *fa); -// dequeues everything into oblivion -``` - -# Enfilage - -## Deux cas différents: - -1. La file est vide (faire un dessin): - -. . . - -{width=40%} - -2. La file n'est pas vide (faire un dessin): - -. . . - -{width=70%} - -# Enfilage - -## Live (implémentation) - -. . . - -```C -void queue_enqueue(queue *fa, int val) { - element* elmt = malloc(sizeof(*elmt)); - elmt->data = val; - elmt->next = NULL; - if (queue_is_empty(*fa)) { - fa->head = elmt; - fa->tail = elmt; - } else { - fa->tail->next = elmt; - fa->tail = elmt; - } -} -``` - -# Défilage - -## Trois cas différents - -1. La file a plus d'un élément (faire un dessin): - -. . . - -{width=80%} - -2. La file un seul élément (faire un dessin): - -. . . - -{width=25%} - - -3. La file est vide (problème) - -# Défilage - -## Live (implémentation) - -. . . - -```C -int queue_dequeue(queue *fa) { - element* elmt = fa->head; - int val = elmt->data; - fa->head = fa->head->next; - free(elmt); - if (NULL == fa->head) { - fa->tail = NULL; - } - return val; -} -``` - -. . . - -## Problème avec cette implémentation? - -# Destruction - -## Comment on faire la désallocation? - -. . . - -On défile jusqu'à ce que la file soit vide! - -# Complexité - -## Quelle sont les complexité de: - -* Enfiler? - -. . . - -* Défiler? - -. . . - -* Détruire? - -. . . - -* Est vide? - - -# Implémentation alternative - -## Comment implémenter la file autrement? - -. . . - -* Données stockées dans un tableau; -* Tableau de taille connue à la compilation ou pas (réallouable); -* `tail` serait un indice du tableau; -* `capacity` serait la capacité maximale; -* On *enfile* "au bout" du tableau, au défile au début (indice `0`). - -. . . - -## Structure de données - -```C -typedef struct _queue { - int *data; - int tail, capacity; -} queue; -``` - -# File basée sur un tableau - -* Initialisation? - -. . . - -```C - - - - -``` - -* Est vide? - -. . . - -```C - - - - -``` - - -* Enfiler? - -. . . - -```C - - - - -``` - -* Défiler? - -. . . - -```C - - - - -``` - -# Complexité - -## Quelle sont les complexités de: - -* Initialisation? - -. . . - -```C - - - - -``` - -* Est vide? - -. . . - -```C - -``` - - -* Enfiler? - -. . . - -```C - - - - -``` - -* Défiler? - -. . . - -```C - - - - -``` - -# Une file plus efficace - -## Comment faire une file plus efficace? - -* Où est-ce que ça coince? - -. . . - -* Défiler est particulièrement lent $\mathcal{O}(N)$. - -## Solution? - -. . . - -* Utiliser un indice séparé pour `head`. - -```C -typedef struct _queue { - int *data; - int head, tail, capacity; -} queue; -``` - -# Une file plus efficace (implémentation) - -## Enfilage - -\footnotesize - -```C -void queue_enqueue(queue *fa, int val) { - if ((fa->head == 0 && fa->tail == fa->capacity-1) || - (fa->tail == (fa->head-1) % (fa->capacity-1))) { - return; // queue is full - } - if (fa->head == -1) { // queue was empty - fa->head = fa->tail = 0; - fa->data[fa->tail] = val; - } else if (fa->tail == fa->capacity-1 && fa->head != 0) { - // the tail reached the end of the array - fa->tail = 0; - fa->data[fa->tail] = val; - } else { - // nothing particular - fa->tail += 1; - fa->data[fa->tail] = val; - } -} -``` - -# Une file plus efficace (implémentation) - -## Défilage - -```C -void queue_dequeue(queue *fa, int *val) { - if (queue_is_empty(*fa)) { - return; // queue is empty - } - *val = fa->data[fa->head]; - if (fa->head == fa->tail) { // that was the last element - fa->head = fa->tail = -1; - } else if (fa->head == fa->capacity-1) { - fa->head = 0; - } else { - fa->head += 1; - } -} -``` - - -# Les listes triées - -Une liste chaînée triée est: - -* une liste chaînée -* dont les éléments sont insérés dans l'ordre. - - - -. . . - -* L'insertion est faite telle que l'ordre est maintenu. - -## Quelle structure de données? - -```C - - - - - -``` - -# Les listes triées - -## Quel but? - -* Permet de retrouver rapidement un élément. -* Utile pour la recherche de plus court chemin dans des graphes. -* Ordonnancement de processus par degré de priorité. - -## Comment? - -* Les implémentations les plus efficaces se basent sur les tableaux. -* Possibles aussi avec des listes chaînées. - -# Les listes triées - -\footnotesize - -## Quelle structure de données dans notre cas? - - -Une liste chaînée bien sûr (oui c'est pour vous entraîner)! - -```C -typedef struct _element { // chaque élément - int data; - struct _element *next; -} element; -typedef element* sorted_list; // la liste -``` - -## Fonctionnalités - -```C -// insertion de val -sorted_list sorted_list_push(sorted_list list, int val); -// la liste est-elle vide? -bool sorted_list_is_empty(sorted_list list); // list == NULL -// extraction de val (il disparaît) -sorted_list sorted_list_extract(sorted_list list, int val); - // rechercher un élément et le retourner -element* sorted_list_search(sorted_list list, int val); -``` - -# L'insertion - -## Trois cas - -1. La liste est vide. - -. . . - -{width=30%} - -. . . - -```C -sorted_list sorted_list_push(sorted_list list, int val) { - if (sorted_list_is_empty(list)) { - list = malloc(sizeof(*list)); - list->data = val; - list->next = NULL; - return list; - } -} -``` - -# L'insertion - -2. L'insertion se fait en première position. - -. . . - -{width=80%} - -. . . - -```C -sorted_list sorted_list_push(sorted_list list, int val) { - if (list->data >= val) { - element *tmp = malloc(sizeof(*tmp)); - tmp->data = val; - tmp->next = list; - list = tmp; - return list; - } -} -``` - -# L'insertion - -3. L'insertion se fait sur une autre position que la première. - -. . . - -{width=70%} - -. . . - -\footnotesize - -```C -sorted_list sorted_list_push(sorted_list list, int val) { - element *tmp = malloc(sizeof(*tmp)); - tmp->data = val; - element *crt = list; - while (NULL != crt->next && val > crt->next->data) { - crt = crt->next; - } - tmp->next = crt->next; - crt->next = tmp; - return list; -} -``` - - diff --git a/slides/cours_12.md b/slides/cours_12.md new file mode 100644 index 0000000000000000000000000000000000000000..08cca9343e429715ed32c62d01ee8648aa55a244 --- /dev/null +++ b/slides/cours_12.md @@ -0,0 +1,792 @@ +--- +title: "File d'attente, liste triée, liste doublement chaînée" +date: "2024-12-16" +--- + +# La file d'attente + +\Huge La file d'attente + +# La file d'attente (1/2) + +* Structure de données abstraite permettant le stockage d'éléments. +* *FIFO*: First In First Out, ou première entrée première sortie. +* Analogue de la vie "réelle"": + * File à un guichet, + * Serveur d'impressions, + * Mémoire tampon, ... + +## Fonctionnalités + + . . . + +* Enfiler: ajouter un élément à la fin de la file. +* Défiler: extraire un élément au devant de la file. +* Tester si la file est vide. + +. . . + +* Lire l'élément de la fin de la file. +* Lire l'élément du devant de la file. +* Créer une file vide. +* Détruire une file. + +# La file d'attente (2/2) + +\footnotesize + +## Implémentation possible + +* La structure de file, contient un pointeur vers la tête et un autre vers le début de la file. +* Entre les deux, les éléments sont stockés dans une liste chaînée. + +{width=80%} + +## Structure de données en C? + +. . . + +```C +typedef struct _element { // Elément de liste + int data; + struct _element* next; +} element; +typedef struct _queue { // File d'attente + element* head; // tête de file d'attente + element* tail; // queue de file d'attente +} queue; +``` + +# Fonctionnalités d'une file d'attente + +## Création et consultations + +. . . + +```C +void queue_init(queue *fa); // head = tail = NULL +bool queue_is_empty(queue fa); // fa.head == fa.tail == NULL +int queue_tail(queue fa); // return fa.tail->data +int queue_head(queue fa); // return fa.head->data +``` + +## Manipulations et destruction + +. . . + +```C +void queue_enqueue(queue *fa, int val); +// adds an element before the tail +int queue_dequeue(queue *fa); +// removes the head and returns stored value +void queue_destroy(queue *fa); +// dequeues everything into oblivion +``` + +# Enfilage + +## Deux cas différents: + +1. La file est vide (faire un dessin): + +. . . + +{width=40%} + +2. La file n'est pas vide (faire un dessin): + +. . . + +{width=70%} + +# Enfilage + +## Live (implémentation) + +. . . + +```C +void queue_enqueue(queue *fa, int val) { + element* elmt = malloc(sizeof(*elmt)); + elmt->data = val; + elmt->next = NULL; + if (queue_is_empty(*fa)) { + fa->head = elmt; + fa->tail = elmt; + } else { + fa->tail->next = elmt; + fa->tail = elmt; + } +} +``` + +# Défilage + +## Trois cas différents + +1. La file a plus d'un élément (faire un dessin): + +. . . + +{width=80%} + +2. La file un seul élément (faire un dessin): + +. . . + +{width=25%} + + +3. La file est vide (problème) + +# Défilage + +## Live (implémentation) + +. . . + +```C +int queue_dequeue(queue *fa) { + element* elmt = fa->head; + int val = elmt->data; + fa->head = fa->head->next; + free(elmt); + if (NULL == fa->head) { + fa->tail = NULL; + } + return val; +} +``` + +. . . + +## Problème avec cette implémentation? + +# Destruction + +## Comment on faire la désallocation? + +. . . + +On défile jusqu'à ce que la file soit vide! + +# Complexité + +## Quelle sont les complexité de: + +* Enfiler? + +. . . + +* Défiler? + +. . . + +* Détruire? + +. . . + +* Est vide? + + +# Implémentation alternative + +## Comment implémenter la file autrement? + +. . . + +* Données stockées dans un tableau; +* Tableau de taille connue à la compilation ou pas (réallouable); +* `tail` serait un indice du tableau; +* `capacity` serait la capacité maximale; +* On *enfile* "au bout" du tableau, au défile au début (indice `0`). + +. . . + +## Structure de données + +```C +typedef struct _queue { + int *data; + int tail, capacity; +} queue; +``` + +# File basée sur un tableau + +* Initialisation? + +. . . + +```C + + + + +``` + +* Est vide? + +. . . + +```C + + + + +``` + + +* Enfiler? + +. . . + +```C + + + + +``` + +* Défiler? + +. . . + +```C + + + + +``` + +# Complexité + +## Quelle sont les complexités de: + +* Initialisation? + +. . . + +```C + + + + +``` + +* Est vide? + +. . . + +```C + +``` + + +* Enfiler? + +. . . + +```C + + + + +``` + +* Défiler? + +. . . + +```C + + + + +``` + +# Une file plus efficace + +## Comment faire une file plus efficace? + +* Où est-ce que ça coince? + +. . . + +* Défiler est particulièrement lent $\mathcal{O}(N)$. + +## Solution? + +. . . + +* Utiliser un indice séparé pour `head`. + +```C +typedef struct _queue { + int *data; + int head, tail, capacity; +} queue; +``` + +# Une file plus efficace (implémentation) + +## Enfilage + +\footnotesize + +```C +void queue_enqueue(queue *fa, int val) { + if ((fa->head == 0 && fa->tail == fa->capacity-1) || + (fa->tail == (fa->head-1) % (fa->capacity-1))) { + return; // queue is full + } + if (fa->head == -1) { // queue was empty + fa->head = fa->tail = 0; + fa->data[fa->tail] = val; + } else if (fa->tail == fa->capacity-1 && fa->head != 0) { + // the tail reached the end of the array + fa->tail = 0; + fa->data[fa->tail] = val; + } else { + // nothing particular + fa->tail += 1; + fa->data[fa->tail] = val; + } +} +``` + +# Une file plus efficace (implémentation) + +## Défilage + +```C +void queue_dequeue(queue *fa, int *val) { + if (queue_is_empty(*fa)) { + return; // queue is empty + } + *val = fa->data[fa->head]; + if (fa->head == fa->tail) { // that was the last element + fa->head = fa->tail = -1; + } else if (fa->head == fa->capacity-1) { + fa->head = 0; + } else { + fa->head += 1; + } +} +``` + +# Listes triées + +\Huge Les listes triées + +# Les listes triées + +Une liste chaînée triée est: + +* une liste chaînée +* dont les éléments sont insérés dans l'ordre. + + + +. . . + +* L'insertion est faite telle que l'ordre est maintenu. + +## Quelle structure de données? + +```C + + + + + +``` + +# Les listes triées + +## Quel but? + +* Permet de retrouver rapidement un élément. +* Utile pour la recherche de plus court chemin dans des graphes. +* Ordonnancement de processus par degré de priorité. + +## Comment? + +* Les implémentations les plus efficaces se basent sur les tableaux. +* Possibles aussi avec des listes chaînées. + +# Les listes triées + +\footnotesize + +## Quelle structure de données dans notre cas? + + +Une liste chaînée bien sûr (oui c'est pour vous entraîner)! + +```C +typedef struct _element { // chaque élément + int data; + struct _element *next; +} element; +typedef element* sorted_list; // la liste +``` + +## Fonctionnalités + +```C +// insertion de val +sorted_list sorted_list_push(sorted_list list, int val); +// la liste est-elle vide? +bool sorted_list_is_empty(sorted_list list); // list == NULL +// extraction de val (il disparaît) +sorted_list sorted_list_extract(sorted_list list, int val); + // rechercher un élément et le retourner +element* sorted_list_search(sorted_list list, int val); +``` + +# L'insertion (1/3) + +## Trois cas + +1. La liste est vide. + +. . . + +{width=30%} + +. . . + +```C +sorted_list sorted_list_push(sorted_list list, int val) { + if (sorted_list_is_empty(list)) { + list = malloc(sizeof(*list)); + list->data = val; + list->next = NULL; + return list; + } +} +``` + +# L'insertion (2/3) + +2. L'insertion se fait en première position. + +. . . + +{width=80%} + +. . . + +```C +sorted_list sorted_list_push(sorted_list list, int val) { + if (list->data >= val) { + element *tmp = malloc(sizeof(*tmp)); + tmp->data = val; + tmp->next = list; + list = tmp; + return list; + } +} +``` + +# L'insertion (3/3) + +3. L'insertion se fait sur une autre position que la première. + +. . . + +{width=70%} + +. . . + +\footnotesize + +```C +sorted_list sorted_list_push(sorted_list list, int val) { + element *tmp = malloc(sizeof(*tmp)); + tmp->data = val; + element *crt = list; + while (NULL != crt->next && val > crt->next->data) { + crt = crt->next; + } + tmp->next = crt->next; + crt->next = tmp; + return list; +} +``` + +# L'extraction (1/3) + +## Trois cas + +1. L'élément à extraire n'est **pas** le premier élément de la liste + +. . . + +{width=70%} + +. . . + +\scriptsize + +```C +sorted_list sorted_list_extract(sorted_list list, int val) { + element *prec = *crt = list; // needed to glue elements together + while (NULL != crt && val > crt->data) { + prec = crt; + crt = crt->next; + } + if (NULL != crt && prec != crt && crt->data == val) { // glue things together + prec->next = crt->next; + free(crt); + } + return list; +} +``` + + +# L'extraction (2/3) + +2. L'élément à extraire est le premier élément de la liste + +. . . + +{width=70%} + +. . . + +\footnotesize + +```C +sorted_list sorted_list_extract(sorted_list list, int val) { + element *prec = *crt = list; // needed to glue elements together + while (NULL != crt && val > crt->data) { + prec = crt; + crt = crt->next; + } + // glue things together + if (NULL != crt && crt->data == val && prec == crt) { + list = list->next; + free(crt); + } + return list; +} +``` + +# L'extraction (3/3) + +3. L'élément à extraire n'est **pas** dans la liste. + * La liste est vide. + * La valeur est plus grande que le dernier élément de la liste. + * La valeur est plus petite que la valeur de `crt`. + +. . . + +On retourne la liste inchangée. + +. . . + +\footnotesize + +```C +sorted_list sorted_list_extract(sorted_list list, int val) { + element *prec = *crt = list; // needed to glue elements together + while (NULL != crt && val > crt->data) { + prec = crt; + crt = crt->next; + } + if (NULL == crt || crt->data != val) { // val not present + return list; + } +} +``` + +# La recherche + + + +```C +element* sorted_list_search(sorted_list list, int val); +``` + +* Retourne `NULL` si la valeur n'est pas présente (ou la liste vide). +* Retourne un pointeur vers l'élément si la valeur est présente. + +. . . + +```C +element* sorted_list_search(sorted_list list, int val) { + // search for element smaller than val + element* pos = sorted_list_position(list, val); + if (NULL == pos && val == list->data) { + return list; // first element contains val + } else if (NULL != pos && NULL != pos->next + && val == pos->next->data) + { + return pos->next; // non-first element contains val + } else { + return NULL; // well... val's not here + } +} +``` + +# La recherche + +## La fonction `sorted_list_position` + +```C +element* sorted_list_position(sorted_list list, int val); +``` + + + +# La recherche + +## Exercice: implémenter + +```C +element* sorted_list_position(sorted_list list, int val); +``` + +. . . + +```C +element* sorted_list_position(sorted_list list, int val) { + element* pos = list; + if (sorted_list_is_empty(list) || val <= list->data) { + pos = NULL; + } else { + while (NULL != pos->next && val > pos->next->data) { + pos = pos->next; + } + } + return pos; +} +``` + +# Complexité de la liste chaînée triée + +## L'insertion? + +. . . + +$$ +\mathcal{O}(N). +$$ + +## L'extraction? + +. . . + +$$ +\mathcal{O}(N). +$$ + +## La recherche? + +. . . + +$$ +\mathcal{O}(N). +$$ + + +# Liste doublement chaînée + +## Application: navigateur ou éditeur de texte + +* Avec une liste chaînée: + * Comment implémenter les fonctions `back` et `forward` d'un navigateur? + * Comment implémenter les fonctions `undo` et `redo` d'un éditeur de texte? + +. . . + +Pas possible. + +## Solution? + +. . . + +* Garder un pointeur supplémentaire sur l'élément précédent et pas seulement le + suivant. + +. . . + +* Cette structure de donnée est la **liste doublement chaînée** ou **doubly + linked list**. + +# Liste doublement chaînée + +\Huge Liste doublement chaînée + +# Liste doublement chaînée + +## Exercices + +* Partir du dessin suivant et par **groupe de 5** + + + +1. Écrire les structures de données pour représenter la liste doublement + chaînée dont le type sera `dll` (pour + `doubly_linked_list`) + +# Liste doublement chaînée + +2. Écrire les fonctionnalités de création et consultation + +```C +// crée la liste doublement chaînée +dll dll_create(); +// retourne la valeur à la position actuelle dans la liste +int dll_value(dll list); +// la liste est-elle vide? +bool dll_is_empty(dll list); +// Est-ce que pos est le 1er élément? +bool dll_is_head(dll list); +// Est-ce que pos est le dernier élément? +bool dll_is_tail(dll list); +// data est-elle dans la liste? +bool dll_is_present(dll list, int data); +// affiche la liste +void dll_print(dll list); +``` + +# Liste doublement chaînée + +3. Écrire les fonctionnalités de manipulation + +```C +// déplace pos au début de la liste +dll dll_move_to_head(dll list); +// déplace pos à la position suivante dans la liste +dll dll_next(dll list); +// déplace pos à la position précédente dans la liste +dll dll_prev(dll list); +``` + +# Liste doublement chaînée + +4. Écrire les fonctionnalités d'insertion + +```C +// insertion de data dans l'élément après pos +dll dll_insert_after(dll list, int data); +// insertion de data en tête de liste +dll dll_push(dll list, int data); +``` + +5. Écrire les fonctionnalités d'extraction + +```C +// extraction de la valeur se trouvant dans l'élément pos +// l'élément pos est libéré +int dll_extract(dll *list); +// extrait la donnée en tête de liste +int dll_pop(dll *list); +// vide la liste +void dll_destroy(dll *list); +``` +