package graph; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; import uf.Elt; // un graphe non orienté est représenté comme un graphe orienté // avec l'invariant que x->y si et seulement si y->x public class Undirected<V> extends Graph<V> { @Override public void addEdge(V x, V y) { super.addEdge(x, y); super.addEdge(y, x); } @Override public void removeEdge(V x, V y) { super.removeEdge(x, y); super.removeEdge(y, x); } // tous les arcs, une seule fois chacun (i.e. x->y mais pas y->x) public Set<Edge<V>> allEdges() { Set<Edge<V>> s = new HashSet<>(); Set<V> seen = new HashSet<>(); for (V u: this.vertices()) { for (V v: this.successors(u)) { if (seen.contains(v)) continue; s.add(new Edge<>(u, v)); } seen.add(u); // on l'ajoute après, au cas où il y ait des boucles } return s; } // test d'acyclicité avec union-find public boolean isAcyclic() { if (this.vertices().isEmpty()) return true; // on construit les classes d'équivalence Map<V, Elt<V>> elt = new HashMap<>(); for (V u: this.vertices()) elt.put(u, new Elt<>(u)); for (Edge<V> e: this.allEdges()) { Elt<V> es = elt.get(e.src), ed = elt.get(e.dst); if (es.find() == ed.find()) return false; es.union(ed); } return true; } // test de connexité avec union-find public boolean isConnected() { if (this.vertices().isEmpty()) return true; // on construit les classes d'équivalence Map<V, Elt<V>> elt = new HashMap<>(); for (V u: this.vertices()) elt.put(u, new Elt<>(u)); for (V u: this.vertices()) { Elt<V> eu = elt.get(u); for (V v: this.successors(u)) eu.union(elt.get(v)); } // puis on vérifie que tous les sommets sont dans la même classe V v0 = this.vertices().iterator().next(); Elt<V> c0 = elt.get(v0); for (V u: this.vertices()) if (elt.get(u).find() != c0) return false; return true; } // composantes connexes, avec un parcours en profondeur public List<List<V>> components() { List<List<V>> r = new LinkedList<>(); HashSet<V> visited = new HashSet<>(); for (V v: this.vertices()) if (!visited.contains(v)) { LinkedList<V> c = new LinkedList<>(); componentsAux(visited, c, v); r.add(c); } return r; } private void componentsAux(HashSet<V> visited, LinkedList<V> c, V v) { Stack<V> stack = new Stack<V>(); stack.push(v); while (!stack.isEmpty()) { v = stack.pop(); if (visited.contains(v)) continue; visited.add(v); c.add(v); for (V w : this.successors(v)) stack.push(w); } } // composantes connexes d'un graphe non orienté // avec un parcours en profondeur public Map<V, Integer> numComponents() { Map<V, Integer> num = new HashMap<>(); int nc = 0; // nombre de composantes for (V v: this.vertices()) if (!num.containsKey(v)) dfs(num, nc++, v); return num; } // parcours en profondeur à partir de v, en attribuant le numéro nc private void dfs(Map<V, Integer> num, int nc, V v) { if (num.containsKey(v)) return; num.put(v, nc); for (V w : this.successors(v)) dfs(num, nc, w); } // ou sans récursivité private void numComponents(Map<V, Integer> num, int nc, V v) { Stack<V> stack = new Stack<V>(); stack.push(v); while (!stack.isEmpty()) { v = stack.pop(); if (num.containsKey(v)) continue; num.put(v, nc); for (V w : this.successors(v)) stack.push(w); } } public static Undirected<Integer> grid(int n, int m) { Undirected<Integer> g = new Undirected<>(); for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) { int v = m*i + j; g.addVertex(v); if (j < m-1) g.addEdge(v, v+1); if (i < n-1) g.addEdge(v, v+m); } return g; } } class TestUndirected { public static void main(String[] args) { } }