On s'intéresse dans cet exercice à des graphes. On rappelle qu'un graphe est composé :
de sommets ;
d'arêtes reliant les sommets.
S'il existe une arête entre deux sommets, on dit qu'ils sont voisins.
Un graphe peut être :
non orienté : les arêtes sont des paires non ordonnées des sommets \(\{u;v\}\) non ordonnées. Dans ce cas, si l'arête \(u \rightarrow v\) existe alors l'arête \(v \rightarrow u\) existe elle aussi;
orienté : les arêtes sont des couples ordonnés de sommets \((u;v)\) et l'on parle plutôt d'arcs. L'arc \((u;v)\) indique que $\(u\) est un prédécesseur de \(v\) et que \(v\) est un successeur de \(u\). Il n'est toutefois pas certain que l'arc \((v;u)\) est existe et que \(u\) soit un voisin de \(v\).
Le graphe \(G\) ci-dessous est orienté : les arcs sont représentés par des flèches.
flowchart LR
A([A]) --> B([B])
B --> A
A --> C([C])
C --> D([D])
C --> A
B --> C
Comme on peut le voir l'arc \((C;D))\) existe mais pas l'arc \((D;C)\). Les autres arcs sont symétriques : ils existent dans les deux directions.
Le graphe \(H\) ci-dessous est non-orienté : les arêtes sont représentés par des traits pleins.
flowchart LR
E([E]) --- F
F([F]) --- G
G([G]) --- E
Dans cet exercice, les graphes sont modélisés par leur liste d'adjacence. Celle-ci associe à chaque sommet la liste de ses voisins.
On représente les listes d'adjacence par des dictionnaires Python1 :
les clés sont les sommets ;
la valeur associée à une clé est la liste des sommets voisins du sommet correspondant à la clé.
Le graphe de l'exemple est ainsi représenté par le dictionnaire :
La représentation des graphes à l'aide de dictionnaires dont les valeurs sont des listes a un inconvénient : deux dictionnaires différents peuvent représenter le même graphe sans être égaux pour Python. En effet, si deux listes de voisins ont les mêmes éléments dans un ordre différent alors elles sont différentes.
Considérons le graphe ci-dessous :
flowchart LR
A([A]) --- B
B([B]) --- C([C])
Les deux dictionnaires g et h représentent ce graphe et pourtant on a g!=h :
Deux représentations
g={"A":["B"],"B":["A","C"],# A puis C"C":["B"],}h={"A":["B"],"B":["C","A"],# C puis A"C":["B"],}print(g==h)# affiche False
3. « Égalité » de graphes
Ecrire la fonction graphes_egaux qui prend en paramètres deux dictionnaires g et h représentant des listes d'adjacences et renvoie le booléen indiquant s'ils représentent le même graphe (mêmes sommets et mêmes arêtes).
On garantit que les valeurs de tous les sommets sont comparables. On peut donc trier la liste de voisins d'un sommet particulier en faisant par exemple sorted(g[sommet]).
La fonction graphes_egaux doit être « symétrique » : egaux(g,h) et graphes_egaux(h,g) doivent toujours renvoyer le même résultat.
Exemples
>>> g={... "A":["B"],... "B":["A","C"],# A puis C... "C":["B"],... }>>> h={... "A":["B"],... "B":["C","A"],# C puis A... "C":["B"],... }>>> faux_h={... "A":["B"],... "B":["C"],# il manque A... "C":["B"],... }>>> graphes_egaux(h,g)True>>> graphes_egaux(g,h)True>>> graphes_egaux(g,faux_h)False>>> graphes_egaux(faux_h,g)False
###(Dés-)Active le code après la ligne # Tests (insensible à la casse) (Ctrl+I)
Entrer ou sortir du mode "deux colonnes" (Alt+: ; Ctrl pour inverser les colonnes)
Entrer ou sortir du mode "plein écran" (Esc)
Tronquer ou non le feedback dans les terminaux (sortie standard & stacktrace / relancer le code pour appliquer)
Si activé, le texte copié dans le terminal est joint sur une seule ligne avant d'être copié dans le presse-papier
la dénomination liste d'adjacence est utilisée dans des ouvrages de référence. Elle peut prêter à confusion car ici on met en œuvre de telles listes à l'aide de listes de dictionnaires. On pourrait aussi utiliser de listes de listes, des listes d'ensembles... ↩
# Tests(insensible à la casse)(Ctrl+I)
(Alt+: ; Ctrl pour inverser les colonnes)
(Esc)