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++) {
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;
}
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
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).
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.
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;
} } }
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;
} }
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
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.
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.
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).
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;
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;
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;
}
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) {
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