Le parcours en largeur n'est pas un pré-requis pour ce TP. Il faut par contre avoir compris les structures de données fournies. Si vous ne les avez pas encore faites, commencez par faire le début du TP1 avant de vous lancer dans le TP2 :
Le TP est à rendre (par email) pour le dimanche 12 décembre à 23h59
La pièce la plus importante sera un fichier tp2-nom-prenom.c
contenant votre programme. Le seul fait que votre programme fonctionne ne suffit pas pour avoir une bonne note. Les points suivants seront également pris en compte :
Les réponses aux questions posées dans le TP (complexité de vos fonctions par exemple) seront insérées comme commentaires dans votre fichier principal.
Si vous le souhaitez, vous pouvez aussi envoyer un rapport de TP. Le format de ce rapport sera au choix, mais par ordre de préférence :
.odt
),
Attention : les points suivants ne rapportent rien, mais ne pas les respecter pourra retrancher jusqu'à 10 points sur la note finale :
-Wall -Werror -Wextra -pedantic -std=c99
Makefile
fourni comme pour le TP1. Il suffit de modifier la variable NOM
pour prendre en compte votre propre fichier.
Nous avons vu (oups, « nous allons voir ») pendant le TD2 l'algorithme du « tri topologique ». Cet algorithme permet de linéariser un graphe orienté sans cycle et fonctionne de la manière suivante : on effectue un parcours en profondeur et on trie les sommets par ordre de « fin[]
» décroissant.
Écrivez la fonction triTopologique
de prototype
int triTopologique(GrapheListe G, int *S) ;
Les consignes sont les suivantes :
S[]
. S'il faut prendre les sommets dans l'ordre 4, 1, 0, 3, 5, 2
, on aura alors S[0]=4, S[1]=1, S[2]=0, S[3]=3, S[4]=5, S[5]=2
.
S
contiendra une « approximation » d'un tri topologique...
(N'oubliez pas de documenter ce que vous faites...)
Estimez la complexité de votre fonction.
Si le tableau S
fourni en argument est égal à NULL
, votre fonction devra seulement chercher si G
a des cycles : elle renverra 0
si G
n'a pas de cycle et un nombre strictement positif si G
a au moins un cycle.
Calculer les composante connexes dans un graphe non-orienté se fait facilement avec un parcours.
(Bonus)
Écrivez une fonction pour calculer les composantes connexes d'un graphe non-orienté :
int composantesConnexes(GrapheListe G, int *CC) ;
G
, et l'argument CC
sera un tableau que l'on remplira avec les numéro de composantes fortement connexes : si le sommet s
est dans la composante connexe c
, alors après l'appel à la fonction on aura CC[s] == c
.
Une composante fortement connexe dans un graphe orienté est un ensemble maximal de sommets qui vérifie la propriété suivante : « il existe un chemin orienté de n'importe quel sommet vers n'importe quel autre ».
Une manière de calculer les composantes fortement connexe du graphe G
est de procéder comme suit :
G
G'
, le transposé de G
G'
, en prenant les sommets par ordre de fin[]
décroissant. (L'ordre de fin[]
est donné par le parcours de G
.)
Programmer la fonction de prototype :
int composantesFortementConnexes(GrapheListe G, int *CC) ;
G
, et l'argument CC
sera un tableau que l'on remplira avec les numéro de composantes fortement connexes : si le sommet s
est dans la composante connexe c
, alors après l'appel à la fonction on aura CC[s] == c
.
Comme pour la question 1, essayer d'avoir un code le plus simple possible et documentez bien ce que vous faites.
Un graphe non-orienté est biparti si on peut séparer ses sommets en deux sous-ensembles S et T qui ont la propriété suivante :
Autrement dit, toutes les arêtes sont incidentes à un sommet de S et un sommet de T.
Pour faire ceci, le plus simple est d'utiliser un parcours en largeur :
(Bonus) Écrivez une fonction
int*biparti(GrapheListe G) ;
Si le graphe n'est pas biparti, votre fonction devra renvoyer NULL
; sinon, elle renverra un tableau contenant, pour chaque sommet, son ensemble :
b[s] == 1
c'est que s
est dans S,
b[s] == -1
c'est que s
est dans T.