Concepts et définitions INFO0902 – Data Structures and Algorithms Graphes Justus H. Piater Graphe Graphes orientés 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). Une arête peut être orientée (directed) ou non. Un graphe est orienté si toutes les arêtes sont orientées. Deux nœuds liés par une arête sont adjacents. Une arête orientée a une origine et une destination. Le degré d’un nœud est le nombre des arêtes incidentes. Un nœud peut avoir des arêtes entrantes et des arêtes sortantes. Exemples ? Concepts et définitions Exemples ? 3 / 40 Concepts et définitions 4 / 40 Propriétés basiques Proposition : Si alors Proposition : Si arêtes, alors est un graphe avec Graphes pondérés arêtes, Les arêtes sont annotées d’une étiquette numérique, son poids. . Exemples ? est un graphe orienté avec BOS . 2704 867 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 . 187 SFO 1846 PVD 849 ORD 802 1464 337 144 740 JFK 621 1258 184 1235 DFW 1391 BWI 1121 LAX 964 1090 2342 MIA Concepts et définitions 5 / 40 Concepts et définitions 6 / 40 TDA graphe TDA, SDD et algorithmes basiques 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) Structure de liste des arêtes Deux conteneurs Nœud : Edge insertEdge(Vertex v, Vertex w, Object o); Object removeVertex(Vertex v); et : • une référence vers sa propre position (ou entrée) dans • une référence vers les données Arête : Object removeEdge(Edge e); • 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 9 / 40 Structure de liste des adjacences Pour éviter le parcours de toutes les arêtes, on représente les incidences explicitement : 10 / 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 : Incidence : Matrice des adjacences : • 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… • 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 espace ? Complexité en temps des méthodes ? Complexité en temps des méthodes ? TDA, SDD et algorithmes basiques TDA, SDD et algorithmes basiques 11 / 40 TDA, SDD et algorithmes basiques 12 / 40 Parcours en profondeur Un exemple • 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 Parcours en profondeur : propriétés Proposition : Les arêtes de découverte forment un arbre couvrant. A B C D E F G H I J K L M N O P TDA, SDD et algorithmes basiques 14 / 40 Parcours en largeur Avancer par « couches » de distance constante du nœud de départ À prouver : Regardons un exemple… • 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 TDA, SDD et algorithmes basiques 16 / 40 Parcours en largeur (suite) Parcours en largeur : propriétés Proposition : Les arêtes de découverte forment un arbre couvrant. 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 Les conteneurs contiennent les nœuds des plus courts chemins de longueur à partir de . Temps de calcul ? Exigences aux structures de données ? 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 Implémentation Plus courts chemins – Dijkstra 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 TDA, SDD et algorithmes basiques 19 / 40 18 / 40 Plus courts chemins (shortest paths) La longueur ou le poids d’un chemin des poids des arêtes de : est la somme 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) . La distance d’un nœud à un nœud est la longueur d’un plus court chemin (chemin de poids minimal) de à , ou . • • méthode gloutonne : recherche « pondérée » en largeur 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 Plus courts chemins – Dijkstra Survol Algorithme 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 à 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 minimale, et l’ajouter à Relaxation : pour tous les mettre à jour . . of do Let be a PQ containing all using the while is not empty do .removeMin() foreach vertex adjacent to do if then voisins de , Regardons un exemple… Plus courts chemins – Dijkstra 22 / 40 Update 23 / 40 as keys. accordingly Plus courts chemins – Dijkstra 24 / 40 Correction Proposition : Quand un nœud insertion dans , . Correction (suite) est choisi pour Puisque est le prochain nœud choisi, Puisque est dans , implique que 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 . , ce qui ce qui contredit notre définition de . 25 / 40 Plus courts chemins – Dijkstra Temps d’exécution 26 / 40 Remarques Pour l’algorithme, il faut préciser les structures de données et leurs implémentations. • 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. 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 Plus courts chemins – Dijkstra 28 / 40 Arbres couvrants minimaux (minimum spanning trees) Arbres couvrants minimaux P.ex., pour connecter un ensemble d’ordinateurs entre eux avec un minimum de cablage : Pour un graphe minimise , trouver un arbre qui . Arbres couvrants minimaux Une propriété clé des ACMs Une propriété clé des ACMs (suite) Proposition : Soient • • Note Cette proposition est vraie même en présence de poids négatifs. un graphe pondéré et connexe, et une partition de disjoints et non vides, 30 / 40 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 Arbres couvrants minimaux 32 / 40 L’algorithme de Kruskal L’algorithme de Kruskal (suite) Regardons un exemple. Note Sa correction est une conséquence de la proposition précédente. 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 Temps d’exécution Proposition : Pour un graphe avec nœuds, l’algorithme de Kruskal prend un temps total de pour fusionner tous les groupes. • 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) Arbres couvrants minimaux 34 / 40 Fusion des groupes des nœuds Pour l’algorithme, il faut préciser les structures de données et leurs implémentations : Le temps total de Kruskal est Arbres couvrants minimaux 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 à . . 35 / 40 Arbres couvrants minimaux 36 / 40 L’algorithme de Prim-Jarník Prim-Jarník : remarques Regardons un exemple. 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 Temps d’exécution ? 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 Il s’agit d’une variation simple de l’algorithme de Dijkstra ; son implémentation est un peu plus simple que celui de Kruskal. the element of the key of in null in Arbres couvrants minimaux 37 / 40 Arbres couvrants minimaux 38 / 40 Graphes Résumé • 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