[C]Exo sur les graphes

Discutez d'informatique ici !
Morpheus
Membre Naturel
Messages: 36
Enregistré le: 02 Fév 2006, 19:38

[C]Exo sur les graphes

par Morpheus » 08 Aoû 2006, 10:15

Bonjour,

J'aurais besoin d'aide pour répondre aux questions demandées dans l'exo car je n'arrive pas à utiliser les fonctions fournies dans l'exo pour coder celles demandées.

Merci par avance

Dans le problème, nous manipulons des graphes, dont les noeuds sont des entiers :
Code: Tout sélectionner
typedef struct noeud {
  int n;
  struct noeud **tab; /*Tableau de pointeurs sur d'autres noeuds
         transitions sortantes du noeud)*/
}Noeud;

typedef struct {
  Noeud *racine;//Designe un noeud du graphe
}Graphe;

On supposera que tous les noeuds du graphe sont accessibles à partir de celui désigné par par le pointeur racine de la structure Graphe.
Sur la figure, le noeud racine contient 1 (montré par la flèche entrante). Même si ce n'est pas le cas sur la figure, deux noeuds différents peuvent contenir le me entier.


Code: Tout sélectionner
 
   --------|
  |          |
  |  5---->4
  | ^ \   ^^
  v/   \ / |
->1     /  |
   \   / \ |
    v /   vv
     3---->2


Dans la suite du problème on supposera que tous les graphes ont un nombre de noeuds majorés par MAXNOEUDS.
On souhaite sauvegarder et relire la structure de graphe dans un fichier texte. Les noeuds y sont décrits en parenthèses. L'entier contenu dans le noeud est écrit après la parenthèse ouvrante. Ensuite, chacun des noeuds fils(peu importe l'ordre), à nouveau entre parenthèse. La sauvegarde est réalisée en effectuant un parcours en profondeur du graphe. Au fur et à mesure que les noeuds sont rencontrés pendant le parcours, ils sont mis dans une table de hachage, qui permet :
-de savoir qu'on est déjà passé par le noeud
-de lui associer un numéro de passage(le premier noeud par lequel le parcours en profondeur est passé a numéro 1, le second 2, etc)
Quand le fils d'un noeud a déjà été rencontré lors du parcours en profondeur, on le remplace par son numéro de passage, écrits entre crochets, dans le fichier.
Par exemple, si le parcours en profondeur passe dans l'ordre par les noeuds contenant 1,3,2,4 et 5, le contenu du fichier sera (1(3(2(4[3][1]))[4])(5[2][4])

Pour éviter d'implanter les tables de hachages, on fait une recherche sur le Web, et on trouve un module C implantant les tables de hachages de manière générique. Le module est composé de 2 fichiers,
tableHachage.h et tableHachage.c. Le fichier tableHachage.h est le suivant :
Code: Tout sélectionner
#ifndef _TABLEHACHAGE_H
#define _TABLEHACHAGE_H

/* Ici pleins de typedef qui ne nous interessent pas, y compris celui
pour un type s'appelant _tableHachage, que nous n'utiliserons jamais
*/
...

/* Le type pour les tables de hachage: c'est un pointeur, mais on ne sait pas
exactement comment sont implantées les tables de hachage, et d'ailleurs on s'en moque
*/

typedef _tableHachage * tableHachage;

/* Cette fonction crée une table de hachage dont on précise la taille.
Retourne 1 si tout se passe bien, 0 sinon;
Au retour de la fonction, *th désigne la table de hachage crée
*/
extern int creerTableHachage(size_t taille,
                                         unsigned int (*fonctionHachage)(void *clef),
                                         unsigned int (*cmpClefs)(void *clef1, void *clef2),
                                         tableHachage *th);

/*Cette fonction insère l'association entre clef et valeur dans la table de hachage th.
Si la clef était déjà présente dans la table, l'ancienne valeur qui lui était associée est écrasé
par la nouvelle.
Elle retourne 1 si l'insertion s'est bien déroulé, 0 sinon
*/
extern int insereDansTableHachage(const void *clef,
                                  const void *valeur,
                                  tableHachage th);

/*Cette fonction retourne un pointeur sur la valeur associée à clef si elle existe, NULL sinon
*/
extern void *rechercheDansTableHachage(const void *clef,
                                       const tableHachage th);

/*Libération de l'espace mémoire pris par une table de hachage
Au retour de la fonction, *th vaut NULL
*/
extern void libereTableHachage(tableHachage *th);

#endif

Dans la suite, nous nous interdison de modifier les fichiers récupérés sur le Web

Exercices :
-L'interface proposé pour les tables de hachage permet-elle de dire si les tables vont faire des copies interne des clefs et des valeurs qu'on y insère ?
Dans les exercices suivants toutes les fonctions retournent un code d'erreur en cas de problème, 0 sinon.

-Ecrire une fonction int ecrireGraphe(const Graphe *g, FILE *fdo) qui écrit le graphe désigné par g dans le flot fdo qu'on suppose ouvert dans le bon mode

-Ecrire une fonction int libereGraphe(Graphe **g) qui libère un graphe qui a entièrement été alloué dynamiquement. Avant de retourner, la fonction doit remettre le pointeur désignant le graphe a NULL



Flodelarab
Membre Légendaire
Messages: 6574
Enregistré le: 29 Juil 2006, 15:04

par Flodelarab » 08 Aoû 2006, 11:50

le contenu du fichier ne serait pas plutot:
(1(3(2(4[3][1]))[4])(5[3][4]))
au lieu
(1(3(2(4[3][1]))[4])(5[2][4])
?
-L'interface proposé pour les tables de hachage permet-elle de dire si les tables vont faire des copies interne des clefs et des valeurs qu'on y insère ?

Si la clef était déjà présente dans la table, l'ancienne valeur qui lui était associée est écrasé
par la nouvelle.

Morpheus
Membre Naturel
Messages: 36
Enregistré le: 02 Fév 2006, 19:38

par Morpheus » 08 Aoû 2006, 14:46

Flodelarab a écrit:le contenu du fichier ne serait pas plutot:
(1(3(2(4[3][1]))[4])(5[3][4]))
au lieu
(1(3(2(4[3][1]))[4])(5[2][4])
?

J'ai vérifié sur l'exo, et c'est bien :
(1(3(2(4[3][1]))[4])(5[2][4]).
5 a pour successeurs 2 et 4

Est-ce que que tu pourrais m'indiquer comme utiliser les fonctions données pour coder la fonction ecrireGraphe ?

Flodelarab
Membre Légendaire
Messages: 6574
Enregistré le: 29 Juil 2006, 15:04

par Flodelarab » 08 Aoû 2006, 16:26

Déjà, BONNE CHANCE! Ya du boulot.

Après, c pas compliqué si tu analyses bien le fait que ta table de hachage sert uniquement à assurer l'unicité de tes noeuds. Tout est à faire.

  1. tu regardes si le noeud est dans la table.
  2. Si oui, tu écris dans ton flux [1234234]
  3. Si non, tu ajoutes le noeud dans la table
  4. tu écris dans ton flux le début du noeud (1234234
  5. tu cherches les enfants et tu les traites en retournant au 1
  6. tu écris dans ton flux la fin du noeud )

j'espere pour toi qu'il n'y a qu'une racine ....

Morpheus
Membre Naturel
Messages: 36
Enregistré le: 02 Fév 2006, 19:38

par Morpheus » 09 Aoû 2006, 11:34

Voilà, ce que j'ai fait.
Je n'affiche que les noeuds mais lorsqu'un noeud a déjà été visité, il faut afficher son numéro de passage
Le souci est que dans l'énoncé, il est dit que c'est la table de hachage qui s'occupe de savoir si on est déjà passé dans un noeud mais je ne sais pas comment faire pour utiliser cela
Code: Tout sélectionner
int ecrireGraphe(const Graph *g, FILE *fdo)
{
  tableHachage th;
  int num_passage = 1, i;

  if((creerTableHachage(MAXNOEUDS, (*fonctionHachage)(void *),
         (*cmpClefs)(void *, void *), &th)) == 0)
    return 0;
 
  while(*g != NULL)
    {
      if((insereDansTableHachage(g->n, num_passage)) == 0)
   return 0;
      else
   {
     fprintf("%d");
     for(i=0; itab[i], fdo);
   }
    }
  return 1;
}

Flodelarab
Membre Légendaire
Messages: 6574
Enregistré le: 29 Juil 2006, 15:04

par Flodelarab » 09 Aoû 2006, 17:14

Morpheus a écrit:Le souci est que dans l'énoncé, il est dit que c'est la table de hachage qui s'occupe de savoir si on est déjà passé dans un noeud mais je ne sais pas comment faire pour utiliser cela
La fonction de recherche dans la table n'est pas la pour la décoration.

Tu n'as pas suivi ma réponse.
Je ne comprends pas ton programme.

Morpheus
Membre Naturel
Messages: 36
Enregistré le: 02 Fév 2006, 19:38

par Morpheus » 10 Aoû 2006, 08:10

Flodelarab a écrit:Déjà, BONNE CHANCE! Ya du boulot.

Après, c pas compliqué si tu analyses bien le fait que ta table de hachage sert uniquement à assurer l'unicité de tes noeuds. Tout est à faire.

  1. tu regardes si le noeud est dans la table.
  2. Si oui, tu écris dans ton flux [1234234]
  3. Si non, tu ajoutes le noeud dans la table
  4. tu écris dans ton flux le début du noeud (1234234
  5. tu cherches les enfants et tu les traites en retournant au 1
  6. tu écris dans ton flux la fin du noeud )

j'espere pour toi qu'il n'y a qu'une racine ....

J'ai réecrit le code avec des commentaires
Code: Tout sélectionner
/*0:OK, 1:erreur*/
int ecrireGraphe(const Graph *g, FILE *fdo)
{
  tableHachage th;/*table de hachage*/
  int i;/*indice de boucle*/
  char *val;/*valeur associee a une cle*/

  /*Creation de la table hachage*/
  if((creerTableHachage(MAXNOEUDS, (*fonctionHachage)(void *),
         (*cmpClefs)(void *, void *), &th)) == 0)
    return 1;
 
  val = recherche(g->racine->n,th);
  /*le noeud n'a pas encore ete visite*/
  if(val == NULL)
    {
      /*on insere dans la table car non present*/
      if(insereDansTableHachage(g->racine->n,val,th) == 0)
   return 1;
      /*affichage du noeud dans le flux*/
      fprintf(fdo,"(%d",g->racine->n);
    }
  /*le noeud a deja ete visite*/
  else
    {
      /*affichage du numero de passage*/
      fprintf(fdo,"[%d]",val);
    }
  /*parcours des fils*/
  for(i=0; iracine->tab[i], fdo);
  fprintf(fdo,")");

  return 0;
}


Flodelarab
Membre Légendaire
Messages: 6574
Enregistré le: 29 Juil 2006, 15:04

par Flodelarab » 10 Aoû 2006, 11:11

ça part bien.

Cependant,
  1. Tu ne gères pas les enfants
  2. Tu ne fermes pas les noeud au bon endroit. Si tu as 2 noeuds et MAXNOEUDS=1000, tu auras 1000 parenthèses fermantes

Morpheus
Membre Naturel
Messages: 36
Enregistré le: 02 Fév 2006, 19:38

par Morpheus » 10 Aoû 2006, 12:44

Flodelarab a écrit:ça part bien.

Cependant,
  1. Tu ne gères pas les enfants
  2. Tu ne fermes pas les noeud au bon endroit. Si tu as 2 noeuds et MAXNOEUDS=1000, tu auras 1000 parenthèses fermantes

Je ne comprends pas bien cette remarque car dans le code, j'écris ceci :
Code: Tout sélectionner
/*parcours des fils*/
  for(i=0; iracine->tab[i], fdo);

J'appelle récursivement la fonction ecrireGraphe sur les fils.

Flodelarab
Membre Légendaire
Messages: 6574
Enregistré le: 29 Juil 2006, 15:04

par Flodelarab » 10 Aoû 2006, 13:15

  • Ok. j'avais pas bien lu
  • Je doute fortement que tu ais MAXNOEUDS enfants
  • La recursivité, c un truc de matheux et pas d'informaticien. C parfait conceptuellement et plaisant pour le cerveau mais une horreur pour le pc... esperons que tu n'auras pas de depassement de pile
  • Et à l'execution? ça devrait commencer à être bien ? non?
  • Tu vas sortir trop tot de ta fonction. Tu sortiras a la premiere branche etudiée a fond.

Patastronch
Membre Irrationnel
Messages: 1345
Enregistré le: 23 Aoû 2005, 00:53

par Patastronch » 10 Aoû 2006, 15:23

Flodelarab a écrit:
  • La recursivité, c un truc de matheux et pas d'informaticien. C parfait conceptuellement et plaisant pour le cerveau mais une horreur pour le pc... esperons que tu n'auras pas de depassement de pile


C'est plus trop le cas aujourd'hui, mais bon c'est hors sujet je m'éterniserai pas la dessus.

Morpheus
Membre Naturel
Messages: 36
Enregistré le: 02 Fév 2006, 19:38

par Morpheus » 10 Aoû 2006, 15:58

Voici la fonction libereGraphe qu'il est demandé de coder dans l'énoncé :
Code: Tout sélectionner
int libereNoeud(Noeud *n){
  int i=0;
  for(i=0; itab[i])!=NULL){
      libererNoeud(n->tab[i]);
      free(n->tab[i]);
    }
  }
  free(n);
  n = NULL;
 
  return 1;
}

int libereGraphe(Graphe **g)
{
  if(*g != NULL)
    {
      libereNoeud(*g->racine);
      free(*g);
    }
 
  free(g);
  g = NULL;

  return 1;
}


la suite de l'exo :
On suppose dans cet exercice que vous avez écrit une fonction
lireGraphe(Graphe **g, FILE *fdi) qui lit le graphe contenu dans un flot ouvert dans le bon mode, et renseigne le pointeur pris en parametre pour qu'il désigne le graphe construit (il est entièrement construit de manière dynamique).

Ecrire un programme copy qui prend en paramètre un nom de fichier texte contenant un graphe sur sa ligne de commande, lit le graphe et le sauve dans un fichier texte dont le nom est passé en second paramètre de la ligne de commande. Les erreurs et les ressources devront être correctement gérées. BIen entendu, votre programme devra utiliser les fonctions que vous avez écrit dans les exercices précédents (lecture du graphe, construction en mémoire de la structure correspondantes, sauvegarde, etc .)

Code: Tout sélectionner
int copy(const char *src, const char *dst)
{
  Graphe *g;
  FILE *fdi, *fdo;
  /*ouverture du fichier en lecture*/
  fdi = fopen(src, "r");
  if(fdi == NULL)
    {
      fprintf(stderr,"erreur de lecture\n");
      return 1;
    }
  /*lecture du graphe*/
  if((lireGraphe(&g, fdi)) == 1)
    {
      fclose(fdi);
      return 1;
    }
  fclose(fdi);

  /*ouverture du fichier en ecriture*/
  fdo = fopen(dst, "w");
  if(fdo == NULL)
    {
      fprintf(stderr,"erreur d'ecriture\n");
      return 1;
    }
  /**ecriture du graphe*/
  if((ecrireGraphe(g, fdo)) == 1)
    {
      fclose(fdo);
      return 1;
    }
  fclose(fdo);
 
  /**liberation de la memoire/
  if((libereGraphe(&g)) == 1)
    return 1;
 
  return 0;
}



Est-ce que quelqu'un pourrait m'indiquer si mes fonctions sont bien écrites, ou bien s'il manque des trucs ?

 

Retourner vers ϟ Informatique

Qui est en ligne

Utilisateurs parcourant ce forum : Aucun utilisateur enregistré et 10 invités

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