Liste Doublement Chainée

Discutez d'informatique ici !
Avatar de l’utilisateur
Rockleader
Habitué(e)
Messages: 2126
Enregistré le: 11 Oct 2011, 18:42

Liste Doublement Chainée

par Rockleader » 22 Fév 2014, 21:03

Salutation,

je réalise l'implémentation d'une liste doublement chaînée (ne possédant qu'une tete d'après l'énoncé, on pourrait également rajouter une queue ça va de soit).


Voici mes structures de base


Code: Tout sélectionner
typedef struct unELEMENT {
int val;
char nom[TAILLE_MAX];
}ELEMENT;

typedef struct uneCEL
{
   ELEMENT e; //élément courant
   struct uneCEL *suiv;//pointeur sur élément suivant
   struct uneCEL *prec;//pointeur sur élément précédent
   
}CEL;

typedef struct uneLISTE {
CEL *tete; //pointeur de tete sur une cellule
}LISTE;

typedef LISTE* LDC;



Je tiens à préciser que le code suivant est correct (sans erreur de syntaxe) mais il ne fonctionne pas parfaitement dans les faits; du coup j'aurais bien aimé savoir ce qui manquait dans mon code ou qui était mal fait.



Je réalise grosso modo; une fonction permettant d'insérer un élément avant la tête de lecture / après la tete de lecture qui seront utilisé par une fonction d'ajout plus générale.

Code: Tout sélectionner
void ajoutElemAvant(LDC *l, ELEMENT e1)
{   
   CEL* maCell=malloc(sizeof(CEL));
   maCell->e=e1;
   if(estVideListe(*l))
   {
      maCell->suiv=NULL;
      maCell->prec=NULL;
      (*l)->tete=maCell;
   }
   else
   {
      maCell->suiv=(*l)->tete;
      maCell->prec=(*l)->tete->prec;
      (*l)->tete->prec=maCell;
   }
}

void ajoutElemApres(LDC *l, ELEMENT e1)
{   
   CEL* maCell=malloc(sizeof(CEL));
   maCell->e=e1;
   if(estVideListe(*l))
   {
      maCell->suiv=NULL;
      maCell->prec=NULL;
      (*l)->tete=maCell;
   }
   else
   {
      maCell->suiv=NULL;
      maCell->prec=(*l)->tete;
      (*l)->tete->suiv=maCell;
   }
}


La fonction utilisant les deux qui est donc appelé dans le main

Code: Tout sélectionner

void ajoutElem(LDC* l, ELEMENT e1)
{
   if (estVideListe(*l))
   {
      ajoutElemApres(l,e1);
   }
   else
   {
      if(e1.val > (*l)->tete->e.val)
      {
         ajoutElemApres(l,e1);
      }
      else
      {
         ajoutElemAvant(l,e1);
      }
   }
}



Et enfin une fonction permettant d'afficher une liste.

Code: Tout sélectionner
void imprimeListe(LDC* l)
{
   LDC* laux;
   laux=l;
   while((*laux)->tete !=NULL)
   {
      printf("%d %s\n",(*laux)->tete->e.val,(*laux)->tete->e.nom);
      (*laux)->tete=(*laux)->tete->prec;
   }
}


Lorsque je tente une exécution dans mon main en faisant l'ajout consécutif de trois élément dans ma suite.
Code: Tout sélectionner
ELEMENT el;
   el.val=10;
   strcpy(el.nom,"Bonjour");
   ajoutElem(&L,el);
ELEMENT el2;
   el2.val=7;
   strcpy(el2.nom,"Coucou");
   ajoutElem(&L,el2);
   ELEMENT el3;
   el3.val=5;
   strcpy(el3.nom,"Salut");
   imprimeListe(&L);


A l'exécution je ne vais afficher que les deux premiers éléments et pas le troisième. Donc je pense que j'ai oublié quelque chose dans le chainage de l'élément précédent.

De plus si je fais un test en faisant en sorte que le second élément que j'insère soit plus grand que le premier, je n'obtiens plus que l'affichage du premier élément.
Donc je dois également avoir un soucis sur l'ajout d'un élément après la tête.


J'ai beau me triturer l'esprit je ne vois pas où mon chaînage merde. A moins que ce ne soit pas fonction d'affichage mais là encore je ne vois pas ce qui pourrait clocher à l'intérieur.
Cette histoire est entièrement vraie puisque je l'ai inventé du début à la fin !



Avatar de l’utilisateur
fatal_error
Membre Légendaire
Messages: 6610
Enregistré le: 22 Nov 2007, 12:00

par fatal_error » 22 Fév 2014, 21:54

hello,

ben tu peux commencer par l'ajouter à ta liste..

Code: Tout sélectionner
   ajoutElem(&L,el2);
   ELEMENT el3;
   strcpy(el3.nom,"Salut");
[COLOR=Green]   ajoutElem(&L,el3);[/COLOR]
   imprimeListe(&L);


note également que ca sert à rien d'implémenter ajouterAvant et ajouterApres, t'en prend une mettons
Code: Tout sélectionner
ajouterApres(a,b){
  ajouterAvant(b,a); //et c'est fini, faut juste faire gaffe aux début et fin de liste
}

edit: pas tenir compte de la digression avec ajouterAvant, j'avais pas compris que c'était un push_front ou push_back.
la vie est une fête :)

Avatar de l’utilisateur
Rockleader
Habitué(e)
Messages: 2126
Enregistré le: 11 Oct 2011, 18:42

par Rockleader » 22 Fév 2014, 22:50

Oh oui, j'avais pas vu que j'oubliais d'ajouter l'élément à la fin --'

Donc effectivement là ça fonctionne pour des valeurs inférieures.

Par contre si je remplace la valeur de el3 à 20; il ne s'affiche plus.

Donc mon soucis de chaînage se trouve sur ajouterApres.
Merci ;)

Je continue à chercher ce qui bloque; pourtant ça me parait correct x) :mur:

EDIT: EN gros; la valeur du premier élément détermine la valeur maximum qui ne peut pas être dépassé. SI je rentre des éléments supérieur à l'affichage ils ne sortent pas; je dois taper sur le pointeur null avant d'arriver au bon endroit à cause d'une erreur de chainage qu'il me reste à repérer =)
Cette histoire est entièrement vraie puisque je l'ai inventé du début à la fin !

Avatar de l’utilisateur
fatal_error
Membre Légendaire
Messages: 6610
Enregistré le: 22 Nov 2007, 12:00

par fatal_error » 22 Fév 2014, 23:15

Code: Tout sélectionner
Elem1Elem2
  ^
 tete

il se passe quoi si tu appeles AjouterApres (Elem3)?
il est stocké ou?
la vie est une fête :)

Avatar de l’utilisateur
Rockleader
Habitué(e)
Messages: 2126
Enregistré le: 11 Oct 2011, 18:42

par Rockleader » 22 Fév 2014, 23:19

fatal_error a écrit:
Code: Tout sélectionner
Elem1Elem2
  ^
 tete

il se passe quoi si tu appeles AjouterApres (Elem3)?
il est stocké ou?


Il se place sur l'élément suivant de ma tete.

EN théorie ma tete->suiv pointe sur NULL.
Donc tete->suiv prend la valeur elem3

L'élément le plus grand de la liste sera forcement la tête puisqu'elle est mise à jour à chaque fois non ?

Merci pour ton aide.
Cette histoire est entièrement vraie puisque je l'ai inventé du début à la fin !

Avatar de l’utilisateur
fatal_error
Membre Légendaire
Messages: 6610
Enregistré le: 22 Nov 2007, 12:00

par fatal_error » 23 Fév 2014, 00:09

EN théorie ma tete->suiv pointe sur NULL.
Donc tete->suiv prend la valeur elem3

chez moi je lis de gauche à droite.
Donc tete->suiv pointe sur Elem2.
Si tu dis tete->suiv=elem3
tu fais quoi de elem2, tu le laisses trainer dans la nature?

L'élément le plus grand de la liste sera forcement la tête puisqu'elle est mise à jour à chaque fois non ?

si le but c'est d'avoir le premier élément de la liste tjs le plus grand, alors tes algo sont un peu pourris. ca marche, mais on peut mieux faire:

Code: Tout sélectionner
procedure ajouter(Liste, Elem)
 if(empty(Liste))
   initListe(Elem)
 else
   if(tete(Liste).value > Elem.value)
     nextElemToTete = elemSuivant(tete)
     tete(Liste).next = Elem <-- on insere entre la tete et l element suivant
     Elem.next=nextElemToTete
   else
     Elem.next=tete
     tete.prev=Elem
     setTete(Elem, L) //on deplace la tete sur notre nouvelle element
   fi
 fi
la vie est une fête :)

Avatar de l’utilisateur
Rockleader
Habitué(e)
Messages: 2126
Enregistré le: 11 Oct 2011, 18:42

par Rockleader » 23 Fév 2014, 11:18

si le but c'est d'avoir le premier élément de la liste tjs le plus grand, alors tes algo sont un peu pourris. ca marche, mais on peut mieux faire:



C'est bien cela le but, si je ne me suis pas planté à chaque fois que j'insère un élément après, il s'insère après la tête qui pointait sur null; et il devient la nouvelle tete qui pointe désormais sur null. ET vu qu'on ne peut le mettre après que s'il est plus grand...

Je suis sur qu'il y a de meilleure façon de faire; j'essaie surtout de comprendre ce qui ne vas pas dans ma méthode ;)
étant donné qu'elle me parait correcte d'un point de vue algorithmique.


EDIT: J'ai repéré l'erreur !!!

(*l)->tete->suiv=maCell;

==>
(*l)->tete=maCell;
Cette histoire est entièrement vraie puisque je l'ai inventé du début à la fin !

Avatar de l’utilisateur
fatal_error
Membre Légendaire
Messages: 6610
Enregistré le: 22 Nov 2007, 12:00

par fatal_error » 23 Fév 2014, 12:41

ce qui va pas dans ta méthode, c'est que tu réutilises pas le code que tu fais.
Du coup, tu prends le risque de rater un copier coller, ou d'avoir une erreur bete à un endroit et pas un autre.
Typiquement ton probleme de tete->suiv correct dans ajouterAvant mais pas ajouterApres

Un exemple très simple (et plus simple déjà que le code que tu proposes)
Code: Tout sélectionner
   if(tete(Liste).value > Elem.value)
     nextElemToTete = elemSuivant(tete)
     tete(Liste).next = Elem suiv = right
  right->prev=left

Ce qui simplifie en
Code: Tout sélectionner
   if(tete(Liste).value > Elem.value)
     nextElemToTete = elemSuivant(tete)
     createLink(tete,elem)
     createLink(elem,nextElemToTete)
   else
     createLink(elem,tete)
     setTete(elem)
   fi

Evidemment il faut gerer le cas ou left et right peuvent etre nuls.
Mais a priori, il suffit que la tete soit allouée, et les éléments que tu insères aussi, apres t'es tranquille...
la vie est une fête :)

Avatar de l’utilisateur
Rockleader
Habitué(e)
Messages: 2126
Enregistré le: 11 Oct 2011, 18:42

par Rockleader » 23 Fév 2014, 13:11

Je prends bien note de ta remarque pour les prochaines fois; j'ai compris ce que tu voulais dire et c'est vrai que j'aurais pu faire quelques fonction de plus.
Après c'est aussi tout le problème des tp; est ce que l'on doit se contenter de ne faire que les fonctions demandé sans passer par des intermédiaires ? Je pense que non (d'où en partie mes fonction d'ajout avant et après qui n'étais pas demandé à la base dans mon sujet^^). Le problème étant au final quand doit on s'arrêter dans la décomposition; à partir de quel moment considère on que cela coute plus cher de réaliser une fonction que de simplement recopier son code.

Parce que corrige moi si je me trompe; mais si on pousse le vice très loin on pourrait créer des fonction totalement inutiles du style int afficheChiffre(int N) qui se contenterait de faire un printf. On est bien d'accord que là ça ne sert à rien car on utilise une fonction pour faire ce qu'une autre fonction fait déjà; et au final on surcharge la pile d'exécution pour rien et on perd en efficacité.

Au final; ou se situe la limite entre coût et performances puisque chaque fonction coûtera de la mémoire
Cette histoire est entièrement vraie puisque je l'ai inventé du début à la fin !

Avatar de l’utilisateur
fatal_error
Membre Légendaire
Messages: 6610
Enregistré le: 22 Nov 2007, 12:00

par fatal_error » 23 Fév 2014, 13:24

Je pense que non (d'où en partie mes fonction d'ajout avant et après qui n'étais pas demandé à la base dans mon sujet^^)

c'est une bonne initiative. Mais tu t'es arrêté en chemin vu que tu as gardé du code duppliqué au sein de tes deux fonctions.
Le problème étant au final quand doit on s'arrêter dans la décomposition; à partir de quel moment considère on que cela coute plus cher de réaliser une fonction que de simplement recopier son code.

Jamais.
Recopier c'est mal. mal. mal.

Si tu as une fonction qui existe déjà, réutilises la.
Si tu as une séquence de fonctions qui existent déjà, et que tu as cette séquence à plusieurs endroits, crèe une fonction qui execute cette séquence.
Si tu as une instruction simple, mais à forte sémantique par exemple
if(l->tete->suiv == NULL) //queue de liste
crèe une fonction... bool queueDeListe(l) //return true si fin de liste

au final on surcharge la pile d'exécution pour rien et on perd en efficacité.

L'efficacité tu la perds dans tes algos essentiellement, pas tant dans tes appels de fonction.

Au final; ou se situe la limite entre coût et performances puisque chaque fonction coûtera de la mémoire

Appeler une fonction ne coute rien en mémoire. Ca coute en tant d'exécution.

Tu noteras que tu peux utiliser le mot clé inline pour remplacer ta fonction par son code (ce qui évite de faire un
appel de fonction), mais mieux vaut laisser le compilateur faire ce genre d'optimisations (ou alors tu as profilé ton code, mais généralement on arrive rarement à ce genre de conclusions...).

Bref, il faut retenir.
Duppliquer le code, c'est mal.
Factorises autant que tu peux.
Donne du sens à ton code.
Appeler des fonctions a un cout très négligeable face à la qualité de tes algorithmes.
la vie est une fête :)

Avatar de l’utilisateur
Rockleader
Habitué(e)
Messages: 2126
Enregistré le: 11 Oct 2011, 18:42

par Rockleader » 23 Fév 2014, 14:02

Très bien, je prends en compte tous ces conseils pour mes prochains travaux x)
Cette histoire est entièrement vraie puisque je l'ai inventé du début à la fin !

 

Retourner vers ϟ Informatique

Qui est en ligne

Utilisateurs parcourant ce forum : Aucun utilisateur enregistré et 1 invité

Tu pars déja ?



Fais toi aider gratuitement sur Maths-forum !

Créé un compte en 1 minute et pose ta question dans le forum ;-)
Inscription gratuite

Identification

Pas encore inscrit ?

Ou identifiez-vous :

Inscription gratuite