[C] Listes chainées - le retour -

Listes chainées - le retour - [C] - Programmation

Marsh Posté le 03-03-2001 à 16:44:48    

Pour rappel, voici le premier TOPIC ou on m'a BEAUCOUP AIDE  
( http://forum.hardware.fr/sqlforum/ [...] ache=cache )
 , et je remercie une nouvelle fois de plus tout le monde.
 
Voila, j'ai repris tout ca, et mon insertion ds l'ordre lexicographique ne fonctionne pas. Je cherche à reperer ou insérer mon article ds ma LC via la fonction détection et je l'insere via la fonction preinsert ( ie : je veux inserer au bon endroit à chaque fois et ne pas faire appel à une fonction de tri total de ma LC ). Le pb, c que ca marche pas. La tete ne rentre pas ds mon tri, et la suite est mal trié, je ne comprends pas. J'ai vraiment essayé bcp de choses, et mon algo me semble correct. Si pouviez y jeter un oeil ca me debloquerait je pense. Merci ! @+
 
Voici le code :  
 
 
#include<stdio.h>  
#include<conio.h>  
#include<alloc.h>  
#include<string.h>  
 
typedef struct art  
   {  
   struct art *suivant;  
   char *nom;  
   char *ref;  
   double prix;  
   int reste;  
   }article;  
 
article* creer();
article* detection(article* t, char* nom);
void inserer(article* t,article* o);
void preinsert(article* t, article* o);
void affiche(article *t);
 
 
 
void main()
   {
   article* tete = NULL;
   article* objet;
   int i=0;
   char rep='o';      
   char buffer[255];  
 
   printf("Creation d'une liste chainee\n" );
   getch();
 
   //while(rep=='o' )
   while ( i < 5 )
      {
      objet=creer();
      //clrscr();
      printf("Donner son nom : " );
 
      scanf(" %s", buffer);
      (*objet).nom = strdup(buffer);
 
      if ( tete==NULL ) tete = objet;  
      else inserer(tete, objet);
      i++;
      //printf("\ncontinuer ? :" );
      //scanf(" %c", &rep);
      }
   getch();
   affiche(tete);
   getch();
 
   }
 
 
 
article* creer()
   {
   article* objet;
   objet = (article*)malloc(sizeof(article));
   if ( objet==NULL )
     {
       printf("Probleme d'allocation memoire !" );
       getch();
     }
   (*objet).nom = NULL;
   (*objet).ref = NULL;
   (*objet).suivant = NULL;
   return objet;
   }
 
void preinsert(article *t, article *o)
   {
   if ( (*t).suivant != NULL )
       {
       (*o).suivant = (*t).suivant;
       }
     (*t).suivant = o;
   }
 
article* detection(article* t, char *nom)
   {
   article* lire = t;
 
   while( lire && ( strcmp( nom,(*lire).nom ) >= 0 ) )
       {
       lire = (*lire).suivant;
       }
       return lire;
   }
 
void inserer(article* t, article *o)
   {
   article* cursor = detection ( t, (*o).nom );
 
   if ( cursor != NULL )
      {
      preinsert ( cursor , o);
      }
    else
       {
         cursor = t; /* on se replace en tete */
         while( (*cursor).suivant != NULL) /* on va a la fin de la lc */
          {
                cursor= (*cursor).suivant;
                }
        (*cursor).suivant = o;
       }
   }
 
void affiche(article *t)
   {
   article *lire=t;
   clrscr();
 
   printf("Etat du stock : \n" );
 
 while( ( lire!=NULL ) )
    {
        printf("\nNom : %s", (*lire).nom);
      //printf("\nReference : %s", (*lire).ref);
      //printf("\nPrix : %4.2lf", (*lire).prix);
      //printf("\nReste : %d", (*lire).reste);
        lire=(*lire).suivant;
      }
 
   printf("\nFin du stock\n\n" );
   getch();
   }

 

--Message édité par Evadream -jbd---

Reply

Marsh Posté le 03-03-2001 à 16:44:48   

Reply

Marsh Posté le 03-03-2001 à 16:51:28    

A noter que je travaille sous Borland C++ 5.

Reply

Marsh Posté le 03-03-2001 à 17:06:52    

Je regarde ça.
 
Avant une analyse plus détaillée, je ferai juste 2 remarques.
1 - Écris "objet->suivant" plutôt que "(*objet).suivant", c'est plus facile à lire... ;)
2 - Tu as besoin d'aller au début et à la fin de ta liste chaînée. Alors je te conseille de créer une deuxième structure "ListeArticle" comme suit :
    typedef struct _ListeArticle {
        article*  tete;
        article*  queue;
    } ListeArticle;
 
Deux avantages :
1 - Quand un paramètre d'une de tes fonctions est censé représenter une liste d'articles, il sera de type "ListeArticles" plutôt que "article". Ca éclaircira grandement tes algorithmes.
2 - Plus besoin de parcourir la liste pour atteindre sa fin : tu as désormais un accès direct !
 
Par ailleurs, ton preinsert(), je l'aurais écrit plutôt comme suit :
    void preinsert(article *t, article *o)
    {
        if (t != NULL) {
            o->suivant = t->suivant;
            t->suivant = o;
        }
    }
 
Ici, ce n'est pas important si t->suivant est NULL. Ce qu'il faut, c'est éviter à tout prix de déréférencer un pointeur NULL, or les seuls pointeurs que tu déréférences ici, c'est o et t. Mais t->suivant n'est pas déréférencé, lui.

Reply

Marsh Posté le 03-03-2001 à 17:09:21    

Ok, je prends note, je regarde de mon coté, merci du temps que tu consacres.

Reply

Marsh Posté le 03-03-2001 à 17:22:41    

Le pb qu'il y a, en plus des remarques précédentes, est que:
- preinsert(t, o) insère o juste APRES t
- détection retourne un pointeur sur l'objet AVANT lequel il faudrait faire l'insertion.

Reply

Marsh Posté le 03-03-2001 à 17:32:45    

Merci, Verdoux, tu as été plus rapide que moi. Pour le coup de preinsert(), je l'avais vu, mais pour detection, il a fallu que j'exécute le programme.
 
Voici donc ce que je te propose, Evadream :
 
#include <stdio.h>
#include <conio.h>
#include <alloc.h>
#include <string.h>
 
typedef struct _Article {
    struct _Article*  suivant;
    char*             nom;
    char*             ref;
    double            prix;
    int               reste;
} Article;
 
typedef struct _ListeArticle {
    Article*  tete;
    Article*  queue;
} ListeArticle;
 
 
ListeArticle* creerListe();
 
Article* creer();
Article* detection(ListeArticle* liste, char* nom);
void     inserer  (ListeArticle* liste, Article* o);
void     preinsert(Article* t, Article* o, ListeArticle* liste);
void     affiche  (ListeArticle* liste);
 
 
 
void main()
{
   ListeArticle*  liste = creerListe();
   Article*       objet;
   int            i   = 0;
   char           rep = 'o';
   char           buffer[255];
 
   printf("Creation d'une liste chainee\n" );
   getch();
 
   //while (rep == 'o') {
   while (i < 5) {
      objet = creer();
      //clrscr();
      printf("Donner son nom : " );
 
      scanf(" %s", buffer);
      objet->nom = strdup(buffer);
 
      inserer(liste, objet);
 
      i++;
      //printf("\ncontinuer ? :" );
      //scanf(" %c", &rep);
   }
 
   getch();
   affiche(liste);
   getch();
}
 
 
ListeArticle* creerListe()
{
   return (ListeArticle*) calloc(1, sizeof(ListeArticle));
}
 
Article* creer()
{
   Article*  objet = (Article*) malloc(sizeof(Article));
 
   if (objet == NULL) {
       printf("Probleme d'allocation memoire !" );
       getch();
   }
 
   objet->nom     = NULL;
   objet->ref     = NULL;
   objet->prix    = 0.0;
   objet->reste   = 0;
   objet->suivant = NULL;
 
   return objet;
}
 
void preinsert(Article* t, Article* o, ListeArticle* liste)
{
    if (t != NULL) {
        o->suivant = t->suivant;
        t->suivant = o;
 
        if (liste->queue == t) {
            liste->queue = o;
        }
    }
}
 
Article* detection(ListeArticle* liste, char* nom)
{
    Article*  cursor = liste->tete;
    Article*  next   = cursor;
 
    while (next != NULL  &&  strcmp(next->nom, nom) < 0) {
        cursor = next;
        next   = next->suivant;
    }
 
    return cursor;
}
 
void inserer(ListeArticle* liste, Article* o)
{
    Article*  cursor;
 
    if (liste->tete == NULL) {
        liste->tete  = o;
        liste->queue = o;
 
        return;
    }
 
    cursor = detection(liste, o->nom);  
 
    if (cursor != NULL) {
        preinsert(cursor, o, liste);
    }
    else {
        liste->queue->suivant = o;
        liste->queue          = o;
    }
}
 
void affiche(ListeArticle* liste)  
{
   Article*  cursor = liste->tete;
 
   clrscr();
 
   printf("Etat du stock : \n" );
 
   while (cursor != NULL) {
       printf("\nNom : %s", cursor->nom);
       //printf("\nReference : %s", cursor->ref);
       //printf("\nPrix : %4.2lf", cursor->prix);
       //printf("\nReste : %d", cursor->reste);
 
       cursor = cursor->suivant;
   }
 
   printf("\nFin du stock\n\n" );  
   getch();  
}

Reply

Marsh Posté le 03-03-2001 à 17:35:05    

Et renommer preinsert() en postinsert() ou insert() tout court, bien sûr...

Reply

Marsh Posté le 03-03-2001 à 23:28:36    

Oulala, merci ! Ca epure bien les choses. Je vais analyser tout ca ! Sinon, le probleme de début n'est pas résolu : l'insertion ds l'ordre lexicographique, ca marche pas. Par exemple, lorsque l'on rentre ds cette ordre : z,d,y,e,b on obtient z,b,e,y,d. Et pourtant, l'algo me semble juste.
 
En tout cas, merci bcp à vous deux.

Reply

Marsh Posté le 03-03-2001 à 23:58:11    

Oui le pb est qu'avec le code proposé on peut pas modifier le premier de la liste.

 

--Message édité par Verdoux--

Reply

Marsh Posté le 04-03-2001 à 00:34:41    

Ah bon ? Mmm, j'ai du mal là. Et puis ca trie mal apres aussi, pas slt le premier de la liste. En tout cas, c plus propre qu'avant :)  
 
Je vois pas pkoi on peut pas bouger le premier de la liste et que ca trie mal apres, mes milliers de dessins sur la meme feuille de papier m'ont pourtant toujours amene à cette solution. Je vois pas comment faire, et puis ce tri, ca commence m'enerve, je me bloque l'esprit dessus depuis 1 semaine, tjs le meme resultat, un truc qui trie pas ;) Le code de BifaceMcLeOD ( encore merci ) avec son autre typedef simplifie enormement la vision de la chose et rend le mecanisme du prog bcp plus simple à comprendre.  
 
OUin =)

Reply

Marsh Posté le 04-03-2001 à 00:34:41   

Reply

Marsh Posté le 04-03-2001 à 00:41:14    

Avec la séquence que t'as rentrée, c'est normal.
D'abord z en tête.
Ensuite on rentre d, on compare à z et là on se dit qu'il faut l'insérer là, mais il est inséré APRES.
Ensuite y, on compare à z et comme le test est tout de suite vérifié (y<z), on l'insère juste après z et avant d.
En fait comme toutes les lettres entrantes sont plus "petites" que z, elles sont insérees juste après z, en poussant celle déjà entrées.
 

 


--Message édité par Verdoux--

Reply

Marsh Posté le 04-03-2001 à 03:55:04    

Bon, j'espère que je ne vais pas dire de bêtises, là...
 
Ton problème, c'est que tu n'arrives pas à insérer avant. Or pour pouvoir le faire, tu as besoin de pouvoir modifier la cellule précédente (ou plus exactement, le pointeur vers la cellule courante, qui est soit dans la cellule précédente, soit, s'il y en a pas, le pointeur sur la tête de liste). Alors que tu n'as accès qu'à la cellule courante et la cellule suivante, mais aucun moyen de remonter vers la cellule précédente. Il faut donc trouver un moyen pour pouvoir modifier cette cellule précédente (plus exactement, le pointeur qui pointe vers la cellule courante ; je sais, je me répète).
 
Tu dois pouvoir t'en sortir soit en faisant retourner un Article** à detection(), soit en faisant en sorte que ta liste chaînée soit doublement chaînée (i.e. un pointeur sur le suivant et aussi un pointeur sur le précédent).
 
Si tu choisis la solution 2, je te laisse voir comment faire (dans ce cas, inutile de modifier detection(), il est très bien comme cela).
 
Si tu choisis la solution 1, il faut que detection() renvoie un pointeur sur le pointeur qui à terme devra pointer sur la cellule à insérer.
 
Donc s'il faut insérer au début de la liste, detection() devra retourner &liste->tete. Sinon, (si je ne me suis trompé dans l'itération que fait detection) &cursor->suivant. Est-ce que c'est clair ? (je sais, là, ça commence à être assez fin...)

Reply

Marsh Posté le 04-03-2001 à 09:26:00    

Je dois avouer que c'est un peu l'angoisse :)

Reply

Marsh Posté le 04-03-2001 à 10:00:45    

void inserer(ListeArticle* liste, Article* o)
{
    Article*  cursor;
 
    if (liste->tete == NULL) {
        liste->tete  = o;
        liste->queue = o;
        return; // je ne comprends ce que vient faire ce return ici, et pourquoi le prog s'arrete à la deuxieme saisie si on l'enleve.
 
    }
 
Sinon, j'ai essaye avec ce que vous m'avez dit ( pointeurs de pointeurs ), j'ai un peu de mal à m'y retrouver, et puis ca le compilo à pas l'air d'aimer que je me plante, j'ai un comportement bizarre de Borland, je compile et puis y me sort un truc que j'ai pas ouvert depuis 2 mois :). J'espere que je vais y arriver
 

 


--Message édité par Evadream -jbd---

Reply

Marsh Posté le 04-03-2001 à 18:10:36    

Si la liste est vide, il suffit de dire "la première et la dernière cellule de la liste sont la même cellule, celle à insérer". Et ça y est, on a fini l'insertion. Voilà à quoi sert le return : à dire "ça y est, on a fini l'insertion".
 
Maintenant, tu peux aussi l'enlever, mais il faut alors que tout le code qui suit dans la fonction inserer() soit dans un else, celui du "if (liste->tete == NULL)".
 
Sinon, la solution du double pointeur n'est pas forcément la plus simple, mais c'est sans doute celle qui te fera le plus apprendre. N'hésite pas à re-publier des morceaux de ton programme si tu ne t'en sors pas...

 

--Message édité par BifaceMcLeOD--

Reply

Marsh Posté le 04-03-2001 à 18:27:11    

Ok, merci bcp !!

Reply

Marsh Posté le 04-03-2001 à 19:54:40    

Tu peux essayer les modifs suivantes:
ListeArticle* creerListe()  
{  
                      ListeArticle* LA;
   LA = (ListeArticle*) malloc(sizeof(ListeArticle));
   LA->tete = NULL;
   LA->queue = NULL;
   return LA;
}  
 
// fonction de comparaison, retourne un int <0, =0 ou >0
int compare_nom(char* nom1, char* nom2) {  
  return strcmp(nom1,nom2);
}
 
 
void inserer(ListeArticle* liste, Article* o) {  
                      Article  *cursor, *next;  
   
  // la liste est vide:
                      if (liste->tete == NULL) {  
                          liste->tete  = o;  
                          liste->queue = o;  
                          return;  
   }
 
  // la liste n'est pas vide, on va voir où ce placer.
   if (compare_nom(o->nom, liste->tete->nom)<0)
    {//on doit se mettre en tete
    o->suivant = liste->tete;
    liste->tete = o;
    return;
    }
    //sinon on continue
   cursor = liste->tete;
   next = liste->tete->suivant;
   while((next!=NULL) && (compare_nom(o->nom,next->nom)>0))
   {
   cursor = next;
   next = next->suivant;
   }
   //le curseur pointe maintenat sur l'article apres lequel doit se faire l'insertion
   o->suivant = cursor->suivant;
   cursor->suivant = o;
 
   //si next = null mettre à jour la queue de la liste.
   if (next==NULL) liste->queue = o;  
}

 

--Message édité par Verdoux--

Reply

Marsh Posté le 04-03-2001 à 20:12:27    

Merci ! Ca fait bcp de choses en peu de temps là =]
Je vais bien analyser tout ca, comparer à ce que j'ai deja fait. Je vous tiens au courant et je vous remercie encore pour le temps que vous m'avez accordé. Bonne soirée et bon courage pour la reprise de la semaine.

Reply

Marsh Posté le 12-03-2001 à 01:25:20    

Je repasse pour vous remercier, c bon c fini, j'ai compris BEAUCOUP de choses !
 
@+

Reply

Marsh Posté le 12-03-2001 à 01:52:24    

Je suis sûr qu'il t'en reste encore à apprendre... ;)
 
Plus sérieusement, repasse quand tu veux, c'était un plaisir de répondre à tes questions. :)

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed