1. Représentation des graphes : a) Listes d'adjacences : #Liste de listes : S=[ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' ] #liste de correspondance G= [ ['b', 'c'], ['a', 'd', 'e'], ['a', 'd'], [ 'c', 'b', 'e'], ['b', 'c', 'e'], ['b', 'd', 'f', 'g'], ['e', 'g'], ['e', 'f'], ['g'] ] #Dictionnaire : G = { 'a': ['b', 'c'], 'b': ['a', 'd', 'e'], 'c': ['a', 'd'],'d' :[ 'c', 'b', 'e'], 'd': ['b', 'c', 'e'], 'e': ['b', 'd', 'f', 'g'], 'f': ['e', 'g'], 'g': ['e', 'f'], 'h': ['g'] } b) Matrice d'adjacences : S= ['a','b','c','d','e'] M = [ [0, 0, 1, 0, 1], [0, 0, 0, 0, 0], [0, 1, 0, 0, 0], [0, 1, 0, 0, 0], [0, 1, 0, 1, 0]] M = [ [0, 0, [0, 0, [0, 0, [0, 1, [0, 0, [0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0], 0], 1], 0], 1], 1]] Pas besoin d’une liste de correspondance dans ce cas ! 2. La structure de données deque : L'implémentation des Piles et Files à l'aide d'une liste : On utilise les méthodes pop et append avec les complexités suivantes : O(1) pour append() et pop(-1) O(n) pour pop(0) L'implémentation des Files et Piles à l'aide de deque(): La structure deque (Doubly Ended Queue) du module collections est préférée à une liste dans les cas où nous avons besoin d’opérations d’ajout et de suppression plus rapides à partir des deux extrémités de la structure, car deque fournit une complexité temporelle O(1) pour toutes les opérations d’ajout et de suppression par rapport à la liste qui peut avoir une complexité temporelle O(n). Méthodes de deque : Méthode append(x) appendleft(x) Rôle append ajoute une valeur du côté droit du deque append ajoute une valeur du côté gauche du deque pop() Supprime et renvoie la dernière valeur du deque popleft() Supprime et renvoie la première valeur du deque clear() Vide le deque Exemple : from collections import deque File = deque() File.append("A") #ajout à la fin de la File File.append("B") #ajout à la fin de la File print(File) #affiche deque(['A', 'B']) File.popleft() #renvoie: 'A' 3. Analyse de la complexité du parcours en largeur : Soit n le nombre de sommets du graphe, et p le nombre d'arcs/arêtes. La version de base du parcours en largeur : def parcoursLargeur(G,s): Parcours = [] File = [ s ] while File != [] : tete = File.pop(0) for v in G[tete]: if v not in File and v not in Parcours : File.append(v) Parcours.append(tete) return Parcours # O(1) # O(1) # O(n) # O(n) # O(1) len(G[tete]) itérations au total n itérations (while) Lors du parcours, chaque sommet entre une seule fois dans la file F, et en sort aussi une seule fois. Donc le nombre total des itérations de la boucle while est n. chaque liste de voisins d'un sommet est parcourue au plus une seule fois, or la somme des longueurs de toutes les listes est égale à 2 fois le nombre d'aretes p ( ou une fois le nombre d'arcs si G est oriénté). On obtient alors un cout en O(p) donc le nombre total des itérations de la boucle for est 2p (ou p si G est oriénté). Complexité : O( n2 + np ) from collections import deque Version améliorée : def parcoursLargeurAmelioree(G,s): Parcours = [] visites = {k: 0 for k in G} #en compréhension, tous les sommets sont initialement non visités (0) : O(n) File = deque([ s ]) visites[ s ] = 1 while File != deque([]) : tete = File.popleft() # n itérations # O(1) for v in G[tete]: if visites[v] == 0 : # p itérations au total # O(1) File.append(v) visites[v] = 1 Parcours.append(tete) return Parcours Complexité : O( n + p ) 4. Applications : a) Détection de la connexité d’un graphe : def connexe(G,s): P=parcoursLargeur(G,s) return len(G)==len(P) Graphe non orienté def connexe(G): for k in G : P=parcoursLargeur(G,k) if len(P)!=len(G) : return False return True Graphe orienté b) Détection de cycles :