INFO0902 – Data Structures and Algorithms Graphes Justus H. Piater Concepts et définitions Graphe Définition : Un graphe est (ici) un ensemble de nœuds (vertices) et un ensemble de paires d’éléments de , les arêtes (edges). Deux nœuds liés par une arête sont adjacents. Le degré d’un nœud est le nombre des arêtes incidentes. Exemples ? Concepts et définitions 3 / 40 Graphes orientés Une arête peut être orientée (directed) ou non. Un graphe est orienté si toutes les arêtes sont orientées. Une arête orientée a une origine et une destination. Un nœud peut avoir des arêtes entrantes et des arêtes sortantes. Exemples ? Concepts et définitions 4 / 40 Propriétés basiques Proposition : Si alors est un graphe avec arêtes, . Proposition : Si est un graphe orienté avec arêtes, alors . Proposition : Soit un graphe simple avec nœuds et arêtes. Le nombre des arêtes est : Si est non-orienté, alors ; si est orienté, alors . Concepts et définitions 5 / 40 Graphes pondérés Les arêtes sont annotées d’une étiquette numérique, son poids. Exemples ? BOS 2704 867 187 SFO 1846 PVD 849 ORD 337 1464 144 740 802 621 JFK 1258 184 1235 DFW 1391 BWI 1121 LAX 964 1090 2342 MIA Concepts et définitions 6 / 40 TDA, SDD et algorithmes basiques TDA graphe Vertices vertices(void); Edges edges(void); Edges incidentEdges(Vertex v); Vertex opposite(Vertex v, Edge e); Array endVertices(Edge e); Boolean areAdjacent(Vertex v, Vertex w); Object replace(Vertex v, Object o); Object replace(Edge e, Object o); Vertex insertVertex(Object o); TDA, SDD et algorithmes basiques 8 / 40 TDA graphe (suite) Edge insertEdge(Vertex v, Vertex w, Object o); Object removeVertex(Vertex v); Object removeEdge(Edge e); TDA, SDD et algorithmes basiques 9 / 40 Structure de liste des arêtes Deux conteneurs Nœud : et : • une référence vers sa propre position (ou entrée) dans • une référence vers les données Arête : • une référence vers sa propre position (ou entrée) dans • une référence vers les données • deux références vers les nœuds connectés Regardons un exemple… Complexité en espace ? Complexité en temps des méthodes ? TDA, SDD et algorithmes basiques 10 / 40 Structure de liste des adjacences Pour éviter le parcours de toutes les arêtes, on représente les incidences explicitement : Incidence : • une référence vers l’arête incidente Au nœud on ajoute une référence vers le conteneur des incidences sur . À l’arête connectant et on ajoute deux références vers les incidences associées à dans et . Regardons un exemple… Complexité en espace ? Complexité en temps des méthodes ? TDA, SDD et algorithmes basiques 11 / 40 Structure de matrice des adjacences Pour déterminer les adjacences entre deux nœuds en temps constant, on ajoute à la structure de liste des arêtes une matrice des adjacences : Matrice des adjacences : • une matrice dont l’élément contient une référence vers l’arête correspondante (ou nulle si elle n’existe pas). À chaque nœud on ajoute un indice (clé) entre et . Regardons un exemple… Complexité en espace ? Complexité en temps des méthodes ? TDA, SDD et algorithmes basiques 12 / 40 Parcours en profondeur • un fil (comme l’utilisa Thésée pour sortir du labyrinthe) • de la peinture (comme Thésée aurait pû l’utiliser pour trouver le Minotaure plus vite) Algorithm DFS( , ): Input: A graph and a vertex of . Output: A labeling of the edges in the connected component of as discovery edges and back edges. label as visited foreach in .incidentEdges( ) do if is unvisited then .opposite( , ) if is unexplored then label as a discovery edge DFS( , ) else label as a back edge TDA, SDD et algorithmes basiques 13 / 40 Un exemple TDA, SDD et algorithmes basiques A B C D E F G H I J K L M N O P 14 / 40 Parcours en profondeur : propriétés Proposition : Les arêtes de découverte forment un arbre couvrant. À prouver : • Tous les nœuds sont atteints. • Les arêtes de découverte ne forment aucun cycle. Temps de calcul ? Exigences aux structures de données ? TDA, SDD et algorithmes basiques 15 / 40 Parcours en largeur Avancer par « couches » de distance constante du nœud de départ Regardons un exemple… TDA, SDD et algorithmes basiques 16 / 40 Parcours en largeur (suite) Algorithm BFS( , ): Input: A graph and a vertex of . Output: A labeling of the edges in the connected component of as discovery edges and cross edges. label as visited; insert it into container while is not empty do create empty container foreach in do foreach in .incidentEdges( ) do if is unexplored then .opposite( , ) if is unexplored then label as a discovery edge label as visited; insert it into else label as a cross edge TDA, SDD et algorithmes basiques 17 / 40 Parcours en largeur : propriétés Proposition : Les arêtes de découverte forment un arbre couvrant. Les conteneurs contiennent les nœuds des plus courts chemins de longueur à partir de . Temps de calcul ? Exigences aux structures de données ? TDA, SDD et algorithmes basiques 18 / 40 Implémentation Comment attacher des attributs (visited, discovery edge, back edge, cross edge) aux nœuds et arêtes : • Prévoir un champ explored ? • Table de hashage externe ? Le design pattern décorable : attacher une application à (ici) la position. Object element(void); Object get(Object key); Object put(Object key, Object value); Object remove(Object key); Entries entries(void); TDA, SDD et algorithmes basiques Plus courts chemins – Dijkstra 19 / 40 Plus courts chemins (shortest paths) La longueur ou le poids d’un chemin des poids des arêtes de : est la somme . La distance d’un nœud à un nœud est la longueur d’un plus court chemin (chemin de poids minimal) de à , ou . Note Attention aux poids négatifs – un plus court chemin n’est pas défini pour un cycle de poids négatif ! Comment trouver les plus courts chemins d’un nœud désigné vers tous les autres nœuds si pour tous les ? Plus courts chemins – Dijkstra 21 / 40 L’algorithme de Dijkstra Pour trouver les plus courts chemins (ici non orientés) d’un nœud désigné vers tous les autres nœuds d’un graphe (arbre des plus courts chemins) – single-source shortest paths (shortest-path tree) • • méthode gloutonne : recherche « pondérée » en largeur Plus courts chemins – Dijkstra 22 / 40 Survol dénote la longueur du plus court chemin de trouvé jusqu’à présent. 1. 2. 3. , et pour « nuage » de nœuds Répéter : a. b. Choisir de à . minimale, et l’ajouter à Relaxation : pour tous les mettre à jour . . voisins de , Regardons un exemple… Plus courts chemins – Dijkstra 23 / 40 Algorithme Algorithm DijkstraShortestPaths( , ): Input: A simple, undirected, weighted graph with nonnegative edge weights, and a distinguished vertex of . Output: A label for each vertex of that gives the distance from to in . foreach vertex of do Let be a PQ containing all using the while is not empty do .removeMin() foreach vertex adjacent to do if then Update as keys. accordingly Plus courts chemins – Dijkstra 24 / 40 Correction Proposition : Quand un nœud insertion dans , . est choisi pour Preuve (par contradiction) : Soit • le premier nœud choisi avec (l’impossibilité de est facilement prouvée par construction), • le plus court chemin de à , • le dernier nœud de dans , • son successeur (le premier nœud de pas dans ). Alors, Plus courts chemins – Dijkstra 25 / 40 Correction (suite) Puisque est le prochain nœud choisi, Puisque est dans , implique que . , ce qui ce qui contredit notre définition de . Plus courts chemins – Dijkstra 26 / 40 Temps d’exécution Pour l’algorithme, il faut préciser les structures de données et leurs implémentations. PQ adaptable avec localisateur basée tas : Temps total de la boucle while : d’après notre proposition. PQ basée séquence non triée : puisque est simple. Plus courts chemins – Dijkstra 27 / 40 Remarques • L’algorithme de Dijkstra s’adapte facilement aux graphes orientés. • L’algorithme de Bellman-Ford est et fonctionne en présence de poids négatifs (orientés) dans le graphe. Plus courts chemins – Dijkstra 28 / 40 Arbres couvrants minimaux Arbres couvrants minimaux (minimum spanning trees) P.ex., pour connecter un ensemble d’ordinateurs entre eux avec un minimum de cablage : Pour un graphe minimise Arbres couvrants minimaux , trouver un arbre qui . 30 / 40 Une propriété clé des ACMs Proposition : Soient • • un graphe pondéré et connexe, et une partition de disjoints et non vides, en deux ensembles • une arête de poids minimal parmi celles connectant et . Alors fait partie d’un arbre couvrant minimal. Preuve (par construction) : Soit un ACM ne comportant pas . On l’ajoute alors à , ce qui crée un cycle dont et une arête connectent et . Puisque (par le choix de ) , on peut recréer un ACM en supprimant . Arbres couvrants minimaux 31 / 40 Une propriété clé des ACMs (suite) Note Cette proposition est vraie même en présence de poids négatifs. Arbres couvrants minimaux 32 / 40 L’algorithme de Kruskal Regardons un exemple. Algorithm KruskalMST( ): Input: A simple, connected, weighted graph with vertices and edges. Output: A minimum spanning tree for . foreach do define an elementary cluster Initialize a PQ containing all with weights as keys. while has fewer than edges do .removeMin() Let , be the clusters containing , . if then Add to . Merge and . Arbres couvrants minimaux 33 / 40 L’algorithme de Kruskal (suite) Note Sa correction est une conséquence de la proposition précédente. Arbres couvrants minimaux 34 / 40 Temps d’exécution Pour l’algorithme, il faut préciser les structures de données et leurs implémentations : • Arêtes dans une file de priorité basé tas : prochaine arête en • Nœuds d’un groupe dans une liste non triée stockés avec une référence vers son propre groupe ; fusion par l’ajout du plus petit groupe au plus grand : en , temps total de pour les fusions (voir le transparent prochain) Le temps total de Kruskal est . Arbres couvrants minimaux 35 / 40 Fusion des groupes des nœuds Proposition : Pour un graphe avec nœuds, l’algorithme de Kruskal prend un temps total de pour fusionner tous les groupes. Preuve : À chaque fusion, la taille du cluster ajouté est multipliée par deux au moins. Soit le nombre de fois un nœud est ajouté à un autre groupe. Alors, . Le temps maximal total pour tous les nœuds est donc proportionnel à Arbres couvrants minimaux . 36 / 40 L’algorithme de Prim-Jarník Algorithm PrimJarníkMST( ): Input: A simple, connected, weighted graph with vertices and edges. Output: A minimum spanning tree for . Pick any vertex of foreach vertex do Initialize a PQ with keys , elements while is not empty do .removeMin() Add vertex , edge to foreach vertex adjacent to do if then Change to Change to the element of the key of in null in Arbres couvrants minimaux 37 / 40 Prim-Jarník : remarques Regardons un exemple. Il s’agit d’une variation simple de l’algorithme de Dijkstra ; son implémentation est un peu plus simple que celui de Kruskal. Temps d’exécution ? Arbres couvrants minimaux 38 / 40 Résumé Graphes • Structures de données : liste des arêtes, liste des adjacences, matrice des adjacences • Parcours en profondeur et en largeur • Plus courts chemins : Dijkstra • Arbres couvrants minimaux : Kruskal et Prim-Jarník Résumé 40 / 40