#include #include int A; #define N 30000 // 100 ou 30000 #define A (4*N) typedef char bool; // 0 ou 1 typedef int sommet; // 0 à N-1 #if N < 400 typedef bool gr1[N][N]; #else typedef bool gr1[10][10]; #endif typedef struct maillon maillon, *liste; struct maillon { sommet s; liste suiv; }; typedef liste gr2[N]; //tableau de liste chaînée. typedef struct {sommet ori,ext;} arc, gr3[A]; //définition des arcs, gr3 est un tableau d'arcs. typedef int gr4[2*A], ts3[N+A], ts4[N+2*A]; #undef A /* Exemple des trois représentations d'un même graphe : gr1 g1={{0,0,1,0},{1,0,1,0},{0,1,0,1},{0,1,0,0}}; maillon m6={1,0}, m5={3,0}, m4={1,&m5}, m3={2,0}, m2={0,&m3}, m1={2,0}; gr2 g2={&m1, &m2, &m4, &m6}; gr3 g3={{0,2},{2,3},{3,1},{1,2},{1,0},{2,1}}; */ //Procédure pour créer un gr3 à partir d'un gr1 void gr1gr3(gr1 g1, gr3 g3) { int i,j,k=0; for(i=0;ij est dans le graphe g1 g3[k++]=(arc){i,j}; //on le met dans g3 } //Procédure pour créer un gr1 à partir d'un gr3 void gr3gr1(gr3 g3, gr1 g1) { int i,j,k; for(i=0;isuiv) g3[k++]=(arc){i,p->s}; } //Procédure pour créer un gr1 à partir d'un gr2 void gr2gr1(gr2 g2, gr1 g1) { int i,j; liste p; for(i=0;isuiv) // O(N+A) g1[i][p->s]=1; // O(A) } liste nouv(int s,liste suiv) { liste a=malloc(sizeof(maillon)); *a=(maillon){s,suiv}; return a; } //Procédure pour créer un gr2 à partir d'un gr1 void gr1gr2(gr1 g1, gr2 g2) { int i,j; for(i=0;isuiv, free(s); for(i=0;i %d ", i, j); printf("\n"); } void Affgr3(gr3 g3) { int k; for(k=0;k %d ", g3[k].ori, g3[k].ext); printf("\n"); } void Affgr2(gr2 g2) { int i; liste s; for(i=0;isuiv) printf("%d -> %d ", i, s->s); printf("\n"); } //TD 2 -------------------- //Chaînage des extrémités des arêtes correspondant à un même sommet. void gr3ts(gr3 g3, ts3 tete) { int i, j, *suiv=tete+N; for(i=0;i"[j0]!=sens) for(j=j0;j<2*A;j+=2) suite[j]=tete[i=g4[j^1]], tete[i]=j; } //Procédure pour transposer la matrice d'adjacence g1. void trans_g1(gr1 g1) { int i,j,tmp; for(i=0;isuiv; j=q->s; //L'arc de i vers j. q->s=i; //l'extremite de l'arc = i. q->suiv=g2[j]; g2[j]=q; } } void Aff_gr4(gr4 g4) { int i; for(i=0;i<2*A;i=i+2) printf("%d<-->%d ", g4[i],g4[i+1]); } // Les trois procédures suivantes sont des exemples d'obtention des // successeurs d'un sommet donné selon la représentation des graphes utilisée. // Elles sont a priori inutiles sauf peut-être pour le débogage. void voisin_gr1(gr1 g1, sommet s) { int i; printf("Les voisins de %d sont : ", s); for(i=0;isuiv) printf(" %d ",p->s); printf("\n"); } void voisin_gr3(gr3 g3, ts3 tete, sommet s) { int i, *suite=tete+N; printf("Les voisins de %d sont :", s); for(i=tete[s];i!=-1;i=suite[i]) printf("%d ", g3[i].ext); printf("\n"); } void voisin_gr4(gr4 g4, ts4 tete, sommet s) { int i, *suite=tete+N; printf("Les voisins de %d sont : ", s); for(i=tete[s];i!=-1;i=suite[i]) printf("%d ",g4[i]); printf("\n"); } //TD 3 ------------------------------------------------------ // Les trois procédures suivantes montrent comment faire un parcours de // graphe en largeur d'abord (Width First Search). // Telles quelles, elles ne servent à rien. Mais en les modifiant un peu, // on peut, par exemple, calculer le plus court chemin d'un sommet donné à // chacun des autres. int wfs2(gr2 g2,sommet depart,int file[N]) //width first search { bool M[N]; int io=0, jo=0; sommet x, y; liste p; for(x=0;xsuiv) if(!M[y=p->s]) M[file[jo++]=y]=1; return jo; /*jo : nombre de sommets que l'on peut atteindre à partir de depart y compris lui même*/ } int wfs3(gr3 g3, sommet depart, int file[N]) //width first search { int tete[N+A], *suiv=tete+N; bool M[N]; int io=0,jo=0; int x,y,k; gr3ts(g3,tete); for(x=0;x=0;k=suiv[k]) if(!M[y=g3[k].ext]) M[file[jo++]=y]=1; return jo; } int wfs1(gr1 g1,sommet depart,int file[N])//width first search { bool M[N]; int io=0, jo=0, j; for(j=0;j=0;k=suiv[k]) if(d[y=g3[k].ext]<0) d[file[jo++]=y]=d[x]+1; return io; } // Les trois procédures suivantes montrent comment faire un parcours de // graphe en profondeur d'abord (Depth First Search). // Telles quelles, elles ne servent pas à grand chose. // Elles rendent l'arbre de parcours, dans le tableau pere : // pere[i]=-1 si le sommet i n'est pas dans l'arbre (si on ne peut pas l'atteindre à partir // de depart). // pere[depart]=depart La racine de l'arbre est le seul sommet de l'arbre sans père. // Pour tout autre sommet i de l'arbre, pere[i] est le père du sommet i dans l'arbre. // Le parcours se fait par une procédure récursive «parcours» incluse dans une autre // procédure. (C'est possible en C gnu comme en pascal) // Le parcours pourrait se faire plus efficacement (2 à 10 fois vite et sans risque de // débordement de la pile d'exécution) par une procédure itérative (et non pas récursive) // voir la procédure nbcfc3 dans le TD suivant. void Profondeurgr2(gr2 g2, int depart,int pere[N]) { void parcours(int s) { liste p; for(p=g2[s];p;p=p->suiv) if(pere[p->s]<0) pere[p->s]=s, parcours(p->s); } int j; for(j=0;j=0;k=suiv[k])// on dépile un sommet et tous ses successeurs if(cc[s=g3[k].ext]<0) // non encore visités cc[pile[ip++]=s]=nbc; // sont empilés et mis dans la même composante. return nbc+1; // Nombre de composantes. On ajoute 1 pour compenser le -1 initial. } int nbcfc3(gr3 g3,int cfc[N]) { int fin[N]; { sommet a, b, c; int pile[N], nbPile=0, nbFin=0, tete[N+A], *suiv=tete+N, k; trans_g3(g3); gr3ts(g3,tete); trans_g3(g3); // On fait les listes des prédécesseurs. for(a=0;a':'*');// On chaîne les voisins (si fin est nul) ou les successeurs de chaque sommet. for(s=N;s--;) cc[s]=-1; for(i=N;i--;) // Pour s=fin[N-1], fin[N-2], ..., fin[0] (ou N-1, N-2, ..., 0 si fin est nul) if(cc[s=fin?fin[i]:i]<0) // si s n'est pas marqué, for(cc[pile[ip++]=s]=++nbc; // c'est le premier sommet d'une nouvelle composante. ip;) // On l'empile, puis tant que la pile n'est pas vide, for(k=tete[pile[--ip]];k>=0;k=suiv[k])// on dépile un sommet et tous ses successeurs if(cc[s=g4[k]]<0) // non encore visités cc[pile[ip++]=s]=nbc; // sont empilés et mis dans la même composante. return nbc+1; // Nombre de composantes. On ajoute 1 pour compenser le -1 initial. } // graphe pseudo-aléatoire non orienté de grande taille // i<-->j est une arête ssi i+j+i*j+i²j² est divisible par N. void set1gr3(gr3 g3) // procédure simple en temps O(N²) { int i,j,ij,k=0; for(i=0;i=0;d=suiv[d]) for(i2=(s+d)%N;i2<2*N;i2+=N) if(1&~i2) if(i=i2/2, j=(s-i+N)%N, i*j%N==ij) if(k++,g3) g3[k-1]=(arc){i,j}; return k; } // modification d'un graphe orienté i-->j devient i-->(j+1)%N void tordgr3(gr3 g3) { int k; for(k=0;k