• Aucun résultat trouvé

Exercice 4 : Listes doublement chaˆın´ ees

N/A
N/A
Protected

Academic year: 2022

Partager "Exercice 4 : Listes doublement chaˆın´ ees"

Copied!
16
0
0

Texte intégral

(1)

Isup 1 - Programmation TD no8 Matthieu Journault 19 juin 2020

Exercice 1 : Tableau dynamique

Dans cet exercice, il vous est demand´e d’implanter une structure de donn´ees permettant de stocker des entiers. Cette structure appel´ee tableau dynamique est repr´esent´ee par une structure C contenant 3 champs : un pointeur sur un tableau contenant les entiers ; un entier indiquant le nombre d’´el´ement stock´e dans le tableau et enfin un entier indiquant la taille allou´ee du tableau. Il est en effet possible que le tableau contienne moins d’´el´ement que sa taille allou´e. Lorsqu’un ´el´ement est ajout´e `a la collection :

— si le tableau est plein (le nombre d’´el´ements stock´es et la taille allou´ee sont les mˆemes), on alloue (avec malloc) un tableau deux fois plus grand, on recopie notre tableau dans le nouveau, on ajoute l’´el´ement dans le nouveau tableau puis on lib`ere l’ancien tableau.

— si le tableau n’est pas plein, on peut ajouter l’´el´ement dans le tableau existant.

Q. 1 D´efinir un type structur´e dynarray contenant : un pointeur content vers un tableau d’entier allou´e dans le tas, un entier size repr´esentant le nombre d’´el´ements dans la structure, un entier alloc repr´esentant la taille allou´ee pour le tableau content.

Solution typedef struct dynarray {

int* content; /* pointeur vers la premi`ere case du tableau */

int size; /* nombre d'´el´ements stock´es dans le tableau */

int alloc; /* taille allou´ee du tableau */

}* dynarray;

Q. 2D´efinir une fonctiondynarray empty();renvoyant un tableau vide.

Solution dynarray empty() {

dynarray rep = malloc(sizeof(struct dynarray));

int* content = malloc(1*sizeof(int));

rep->content = content;

rep->size = 0;

rep->alloc = 1;

return rep;

}

Q. 3 D´efinir une fonction void print_dynarray(dynarray tab); permettant un affichage ´ecran du tableau pass´e en argument.

Solution void print_dynarray(dynarray tab) {

printf("{");

if (tab->size > 0) {

for (int i = 0; i < tab->size-1; i++) {

(2)

printf("%d, ", tab->content[i]);

}

printf("%d}", tab->content[tab->size-1]);

} else {

printf("}");

} }

Q. 4D´efinir une fonction int is_in(dynarray tab, int e);permettant de tester l’appartenance de l’´el´ement e au tableau dynamique tab.

Solution int is_in(dynarray tab, int e) {

for (int i = 0; i < tab->size-1; i ++) {

if (tab->content[i] == e) {return 1;}

}

return 0;

}

Q. 5 D´efinir une fonction void add(dynarray tab, int e); permettant l’ajout d’un ´el´ement `a la structure.

Solution void resize(dynarray tab) {

if (tab->size == tab->alloc) { int newalloc = 2 * tab->alloc;

int* newcontent = malloc(newalloc*sizeof(int));

int* oldcontent = tab->content;

int i;

for (i = 0 ; i < tab->size ; i ++) { newcontent[i] = oldcontent[i];

} /* 2 */

tab->content=newcontent;

tab->alloc=newalloc;

free(oldcontent);

tab->content=newcontent;

} }

void add(dynarray tab, int e) { resize(tab);

tab->content[tab->size] = e;

tab->size = tab->size+1;

}

(3)

Q. 6D´efinir une fonctionvoid remove(dynarray tab, int e);permettant la suppression d’un ´el´ement de la structure.

Solution void shift_left(int* tab, int i, int len) {

for (int j = i; j < len-1; j ++) { tab[j] = tab[j+1];

} }

void rem(dynarray tab, int e) { int i;

for (i = 0; tab->content[i] != e && i < tab->size; i++) {}

if (i != tab->size) {

shift_left(tab->content, i, tab->size);

tab->size = tab->size-1;

} }

Q. 7Pour la fonction main suivante : dynarray d = empty();

add(d, 0);

add(d, 1);

add(d, 2);

add(d, 3); /* 1 */

add(d, 4); /* 3 */

Repr´esenter l’´evolution de la m´emoire aux points 1, 2, 3 en supposant que le point 2 se trouve au milieu de votre fonctionadd, apr`es que le contenu du nouveau tableau a ´et´e rempli avec le contenu de l’ancien.

Solution

1 : Stack Heap

main d: @0

@0

content: @3 size: 4 alloc: 4

@3 0 1 2 3

(4)

2 : Stack Heap main d: @0

add e: 4

tab: @0 resize

i: 4

newalloc: 8 newcontent: @4 oldcontent: @3

tab: @0

@0

content: @3 size: 4

alloc: 4

@3 0 1 2 3

@4 0 1 2 3 ? ? ? ?

3 : Stack Heap

main d: @0

@0

content: @4 size: 5 alloc: 8

@4 0 1 2 3 4 ? ? ?

Exercice 2 : Listes chaˆın´ ees

Une liste est une structure de donn´ees qui repr´esente une collection de valeurs. L’un des int´erˆet des listes par rapport `a un tableau est qu’il est ais´e de supprimer ou ajouter des nouveaux ´el´ements entre les ´el´ements d´ej`a pr´esents dans la liste, ainsi que le fait que la taille d’une liste n’est pas strictement d´efinie `a sa cr´eation (et qu’elle peut donc changer au fil de l’ex´ecution du programme).

Le type d’une liste (simplement) chaˆın´ee qui contient des valeurs enti`eres peut ˆetre d´efini comme suit :

struct _int_list { int value;

struct _int_list* next;

};

Dans la suite, le type liste int sera un alias d’un pointeur vers unstruct int list : struct _int_list {

int value;

struct _int_list* next;

};

Chaque ´el´ement d’une liste simplement chaˆın´ee contient une valeur, et un pointeur vers la suite de la liste (i.e. l’´el´ement suivant).

(5)

Q. 8 Impl´ementer une fonction int_list push(int value, int_list list); qui permet d’ajouter un nouveau noeud dans une liste : L’astuce pour cr´eer une liste r´eduire `a un seul ´el´ement d’utiliser liste int liste = ajout debut(34,NULL) pour cr´eer une liste qui contient juste la valeur 34.

Solution int_list push(int value, int_list list) {

int_list newblock = malloc(sizeof(struct _int_list));

newblock->value=value;

newblock->next=list;

return newblock;

}

Q. 9Impl´ementer une fonction int_list empty();retournant la liste vide.

Solution int_list empty() {

return NULL;

}

Q. 10 Impl´ementer une fonction int is_empty(int_list l); permettant de tester si une liste est vide.

Solution /*+END_CORRECTION */

int is_empty(int_list l) { return (l == NULL);

}

Q. 11Afin de faciliter la manipulation des listes dans la suite d´efinir des macros :

— Cons(X, L) permettant de construire une liste ayant comme premi`ere valeur X et comme liste suivante X;

— Empty permettant de construire la liste vide ;

— Head(L) permettant de r´ecup´erer la valeur de l’´el´ement en tˆete de liste

— Tail(L) permettant de r´ecup´erer la sous liste des ´el´ements qui suive l’´el´ement en tˆete.

Solution

#define Cons(X, Y) (push(X, Y))

#define Empty (empty())

#define Head(L) (L->value)

#define Tail(L) (L->next)

Q. 12Impl´ementer une version r´ecursive et une version it´erative de la fonctionvoid print_list(int_list list);

permettant l’affichage de la liste. Utiliser cette fonction et les macros d´efinies ci-avant pour v´erifier que vos fonctions fonctionnent.

(6)

Solution void print_list(int_list list) {

if (is_empty(list)) { printf("[]");

} else {

printf("[");

int_list ptr;

for (ptr = list; ptr->next != NULL; ptr = ptr->next) { printf("%d, ", ptr->value);

}

printf("%d]", ptr->value);

} }

void print_list_rec(int_list list) { if (!is_empty(list)) {

printf("%d, ", Head(list)); print_list_rec(Tail(list));

} }

Q. 13Impl´ementer une version r´ecursive et une version it´erative de la fonctionvoid free_list(int_list list);

permettant de lib´erer l’espace occup´e par la liste pass´ee en argument.

Solution void free_list(int_list list) {

if (is_empty(list)) { return ;

} else {

free_list(list->next);

free(list);

} }

void free_list_rec(int_list list) { if (is_empty(list)) {

return ; } else {

int_list next;

while (list != NULL) { next = list->next;

free(list);

list = next;

} } }

(7)

Q. 14 Impl´ementer une fonction r´ecursive int size_list(int_list list); calculant la taille d’une liste.

Solution int size(int_list list) {

if (is_empty(list)) {return 0;}

else {return 1 + size(Tail(list));}

}

Q. 15Impl´ementer une fonctionint remove_index(int_list list, int i); permettant de suppri- mer l’´el´ement d’indice i de la liste list.

Solution int_list remove_index(int_list list, int i) {

struct _int_list* ptr = list;

struct _int_list* prev = NULL;

int j = 0;

while (ptr != NULL && j < i) { j++;

prev = ptr;

ptr = ptr->next;

}

if (ptr == NULL) {return list;}

else if (prev == NULL) { int_list next = ptr->next;

free(ptr);

return next;

} else {

prev->next = ptr->next;

free(ptr);

return list;

} }

Q. 16On vous donne les trois fonctions suivantes calculant la concat´enation de deux listes.

int_list concatenation1(int_list l1, int_list l2) { if (is_empty(l1)) {return l2;}

else {

int_list ptr;

for (ptr = l1; ptr->next != NULL; ptr=ptr->next) {}

ptr->next = l2;

return l1;

} }

(8)

int_list concatenation2(int_list l1, int_list l2) { if (is_empty(l1) && is_empty(l2)) {return Empty;}

else if (is_empty(l1)) {return l2;}

else { return Cons(Head(l1), concatenation2(Tail(l1), l2)); } }

int_list concatenation3(int_list l1, int_list l2) { if (is_empty(l1) && is_empty(l2)) {return Empty;}

else if (is_empty(l1)) {return Cons(Head(l2), concatenation3(l1, Tail(l2)));}

else { return Cons(Head(l1), concatenation3(Tail(l1), l2)); } }

Pour chacune des trois fonctions donner une repr´esentation de la m´emoire au point de programme /* 1 */ en supposant que la fonction main soit la suivante :

int main() {

int_list l1 = Cons(1, Empty) ; int_list l2 = Cons(2, Empty) ;

int_list l = concatenation(l1, l2); /* 1 */

}

Solution

1 : Stack Heap

main l: @0 l1:@0 l2:@1

@0value:1 next: @1

@1value:2 next: NULL

2 : Stack Heap

main l: @2 l1:@0 l2:@1

@0value:1 next: NULL

@1value:2 next: NULL

@2value:1 next: @1

(9)

3 : Stack Heap main

l: @3 l1:@0 l2:@1

@0value:1 next: NULL

@1value:2 next: NULL

@2value:2 next: NULL

@3value:1 next: @2

Q. 17D´efinir une fonction int_list reverse(int_list list); imp´erative inversant une liste.

Solution int_list reverse(int_list list) {

int_list prev = NULL;

int_list cur = list;

int_list next;

while (cur != NULL) { next = cur->next;

cur->next = prev;

prev = cur;

cur = next;

}

return prev;

}

Q. 18 D´efinir une fonction int_list rev_append(int_list list, int_list acc) r´ecursive retour- nant une copie du renversement de la listlist concat´en´e `a la liste acc.

Solution

int_list rev_append(int_list list, int_list acc) { if (is_empty(list)) {

return acc;

} else {

return rev_append(Tail(list), Cons(Head(list), acc));

} }

Q. 19 En d´eduire une fonction int_list reverse2(int_list list) retournant une copie renvers´ee de la list list.

(10)

Solution int_list reverse2(int_list list) {

return rev_append(list, Empty);

}

Exercice 3 : Tri fusion

Le tri fusion est un algorithme de tri fonctionnement r´ecursivement de la mani`ere suivante : Pour trier une liste de n´el´ements :

— s´eparer la liste en deux sous listes de mˆeme taille (`a 1 pr`es) ;

— trier r´ecursivement les deux sous listes ;

— fusionner intelligemment les deux sous listes ainsi tri´ees.

Ce que l’on entend par une fusion intelligente est : ´etant donn´e deux listes tri´ees on fusionne en produisant une liste tri´ee contenant tous les ´el´ements des deux sous listes. Une telle fusion peut ˆetre implant´ee assez facilement r´ecursivement en regardant uniquement les ´el´ements en tˆete des deux listes.

Q. 20Implanter une fonctionint_list split(int_list list);modifiant en place la listelistpour que celle-ci ne contienne plus qu’un ´el´ement sur deux de la liste list initial et retournant un pointeur vers une liste contenant les ´el´ements manquants.

Solution int_list split(int_list list) {

int_list snd = NULL;

int_list curs = list;

int_list next ;

while (curs != NULL) { next = curs->next;

if (next != NULL) {

curs->next = next->next;

} else {

curs->next = NULL;

}

if (snd == NULL) { snd = next;

}

curs = next;

}

return snd;

}

Q. 21 Implanter une fonction int_list merge(int_list l1, int_list l2); prenant en argument deux listesl1 et l2 que vous supposerez tri´ees et modifiant ces deux listes en places pour produire une liste tri´ee contenant les ´el´ements de l1 etl2. Indication : proposer une d´efinition r´ecursive.

(11)

Solution int_list merge(int_list l1, int_list l2) {

if (is_empty(l1)) { return l2;

} else if (is_empty(l2)) { return l1;

} else {

int hd1 = Head(l1);

int hd2 = Head(l2);

if (hd1 < hd2) {

int_list l = merge(Tail(l1), l2);

l1->next = l;

return l1;

} else {

int_list l = merge(l1, Tail(l2));

l2->next = l;

return l2;

} } }

Q. 22En d´eduire une fonctionint_list sort(int_list l);triant une liste au moyen de l’algorithme de tri fusion propos´e ci-avant.

Solution int_list sort(int_list l) {

if (is_empty(l)) { return NULL;

} else if (is_empty(Tail(l))) { return l;

} else {

int_list l2 = split(l);

l=sort(l);

l2=sort(l2);

int_list res = merge(l, l2);

return res;

} }

Exercice 4 : Listes doublement chaˆın´ ees

Nous avons vu qu’il ´etait difficile d’acc´eder aux derniers ´el´ements d’une liste chaˆın´ee (n´ecessit´e de parcourir toute la liste). Ceci peut ˆetre ennuyeux lorsqu’on aimerait avoir acc`es `a la fois au d´ebut et `a la fin d’une liste (par exemple pour repr´esenter une file d’attente o`u les gens entrent d’un cˆot´e et sortent de l’autre).

(12)

Dans cet exercice nous d´efinissons une nouvelle structure de donn´ee, les listes doublement chaˆın´ees permettant un acc`es `a la fois aux ´el´ements en tˆete et en fin de liste.

Une liste doublement chain´ees sera donc un pointeur vers une structure contenant : head un pointeur vers la tˆete de la liste ;

tail un pointeur vers la fin de la liste ;

De plus afin de faciliter la manipulation de la liste (par exemple, la suppression du dernier ´el´ement) il est important de garder en m´emoire l’´el´ement pr´ec´edant le dernier ´el´ement de la liste (sous peine d’avoir

`

a reparcourir toute la liste pour devoir trouver le nouveau dernier ´el´ement). Ainsi en plus de l’usuel pointeur vers le prochain ´el´ement de la liste nous devons garder un m´emoire pour chaque ´el´ement, un pointeur vers l’´el´ement le pr´ec´edant dans la liste. Ainsi chaque composante de notre liste aura :

elem un entier ;

prev un pointeur vers l’´el´ement de la liste qui le pr´ec`ede.

next un pointeur vers l’´el´ement de la liste qui le suit.

Ci-dessous une repr´esentation graphique d’une telle liste doublement chaˆın´ee contenant les ´el´ements 1,2,3 dans cet ordre.

0 NULL

elem prev next

1

elem prev next

2 NULL

elem prev next

head tail

Q. 23D´efinir la structurestruct dlistscontenant un entierelem, un pointeurprev vers unestruct dlists et un pointeur next vers unestruct dlists.

Solution struct dlists {

int elem ;

struct dlists* next;

struct dlists* prev;

};

Q. 24 D´efinir le type dlist comme ´etant une structure contenant un pointeur head vers une struct dlist et un pointeur tail vers unestruct dlist.

Solution typedef struct dlist {

struct dlists* head;

struct dlists* tail;

}* dlist;

(13)

Q. 25 D´efinir une fonction dlist empty(); renvoyant une dlist vide. La liste vide ne sera pas repr´esent´ee par un pointeur NULL mais par un pointeur vers une structure dont les deux champs head ettail sont des pointeurs NULL.

Solution dlist empty() {

dlist rep = malloc(sizeof(struct dlist));

rep->head=NULL;

rep->tail=NULL;

return rep;

}

Q. 26D´efinir une fonction int is_empty(dlist dl); permettant de tester si une liste est vide.

Solution int is_empty(dlist dl) {

return (dl->head==NULL && dl->tail==NULL);

}

Q. 27 D´efinir une fonction void print_dlist(dlist dl); permettant l’affichage de la liste pass´ee en argument.

Solution void print_dlist(dlist dl) {

if (is_empty(dl)) { printf("[]");

} else {

printf("[<->");

struct dlists* ptr;

for (ptr = dl->head ; ptr != NULL ; ptr = ptr->next) { printf(" %d <->", ptr->elem);

}

printf("]");

} }

Q. 28 D´efinir une fonction void push_head(int elem, dlist dl); permettant l’ajout en place d’un

´el´ement en tˆete de liste.

Solution void push_head(int elem, dlist dl) {

struct dlists* newblock = malloc(sizeof(struct dlists));

newblock->elem = elem;

newblock->prev = NULL;

if (is_empty(dl)) { newblock->next = NULL;

(14)

dl->tail = newblock;

} else {

dl->head->prev = newblock;

newblock->next = dl->head;

}

dl->head = newblock;

}

Q. 29D´efinir une fonctionint pop_head(int elem, dlist dl); permettant la suppression en place d’un ´el´ement en tˆete de liste, on retournera l’´el´ement ainsi supprim´e.

Solution int pop_head(dlist dl) {

int rep = dl->head->elem;

if (dl->head->next == NULL) { free(dl->head);

dl->head = NULL;

dl->tail = NULL;

} else {

dl->head->next->prev = NULL;

struct dlists* newhead = dl->head->next;

free(dl->head);

dl->head=newhead;

}

return rep;

}

Q. 30 D´efinir une fonction void push_tail(int elem, dlist dl); permettant l’ajout en place d’un

´el´ement en fin de liste.

Solution void push_tail(int elem, dlist dl) {

struct dlists* newblock = malloc(sizeof(struct dlists));

newblock->elem = elem;

newblock->next = NULL;

if (is_empty(dl)) { newblock->prev = NULL;

dl->head = newblock;

} else {

dl->tail->next = newblock;

newblock->prev = dl->tail;

}

dl->tail = newblock;

}

(15)

Q. 31D´efinir une fonctionint pop_tail(int elem, dlist dl); permettant la suppression en place d’un ´el´ement en fin de liste, on retournera l’´el´ement ainsi supprim´e.

Solution int pop_tail(dlist dl) {

int rep = dl->tail->elem;

if (dl->tail->prev == NULL) { free(dl->tail);

dl->tail = NULL;

dl->head = NULL;

} else {

dl->tail->prev->next = NULL;

struct dlists* newtail = dl->tail->prev;

free(dl->tail);

dl->tail=newtail;

}

return rep;

}

Q. 32 D´efinir une fonctionvoid reverse(dlist dl);permettant de renverser en place la liste pass´ee en argument.

Solution void reverse(dlist dl) {

struct dlists* tmp = dl->head;

dl->head = dl->tail;

dl->tail = tmp;

if (!is_empty(dl)) { struct dlists* ptr;

for (ptr = dl->head ; ptr != NULL ; ptr = ptr->next) { tmp = ptr->next;

ptr->next = ptr->prev;

ptr->prev = tmp;

} } }

Q. 33D´efinir une fonctionvoid rem(dlist dl, int e); permettant la suppression de la valeur e de la listedl, on pourra supposer que e apparaˆıt au plus une fois dans dl.

Solution void rem(dlist dl, int e) {

struct dlists* cursor;

for (cursor = dl->head; cursor != NULL && cursor->elem != e; cursor = cursor->next) {};

if (cursor != NULL) {

if (cursor->prev == NULL) {

(16)

dl->head=cursor->next;

} else {

cursor->prev->next = cursor->next;

}

if (cursor->next == NULL) { dl->tail=cursor->prev;

} else {

cursor->next->prev = cursor->prev;

}

free(cursor);

} }

p

Références

Documents relatifs

L’exemple suivant montre la déclaration d’une variable de type pointeur de fonction qui doit retourné un entier nommé pFonction1 et d’une fonction retournant

20 Implanter une fonction int_list split(int_list list); modifiant en place la liste list pour que celle-ci ne contienne plus qu’un ´ el´ ement sur deux de la liste list initial

Réaliser une analyse de l’arbre généalogique suivant pour déterminer quel risque pour III4 d’avoir un enfant malade. Les femmes sont symbolisées par des ronds, et les hommes par

Les réactifs sont les ions Ag + et le cuivre métallique car les courbes correspondantes ont un coefficient directeur négatif.. Les produits sont le métal Ag et les ions Cu 2+ car

Un régulateur est dit à action proportionnelle (P) lorsque la valeur de sa tension de sortie est proportionnelle à l’erreur détectée .Quand l’erreur a été corrigée,

3- Ne cessant d’améliorer notre commande, nous avons constaté qu’un phénomène d’oscillation de l’eau autour d’un niveau provoque de nombreux démarrage et arrêt

On décompose le volume du liquide en rotation en couronnes cylindriques de rayon r, d’épaisseur dr et de hauteur z(r). Exprimer le volume dV d’une telle couronne. En supposant que

ANIS ELBAHI 4SCX-MAT-TECH Page 5 24 - Trier un tableau T (de N entiers) de façon. décroissante en utilisant le tri