// Edmond-Karp algorithm is an implementation of the Ford-Fulkerson method // to compute max-flow between a pair of source-sink vertices in a weighted graph // It uses BFS (Breadth First Search) to find the residual paths // Time Complexity: O(V * E^2) where V is the number of vertices and E is the number of edges // Space Complexity: O(V + E) Because we keep residual graph in size of the original graph // Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein. 2009. Introduction to Algorithms, Third Edition (3rd. ed.). The MIT Press. package graph import ( "math" ) // Returns a mapping of vertices as path, if there is any from source to sink // Otherwise, returns nil func FindPath(rGraph WeightedGraph, source int, sink int) map[int]int { queue := make([]int, 0) marked := make([]bool, len(rGraph)) marked[source] = true queue = append(queue, source) parent := make(map[int]int) // BFS loop with saving the path found for len(queue) > 0 { v := queue[0] queue = queue[1:] for i := 0; i < len(rGraph[v]); i++ { if !marked[i] && rGraph[v][i] > 0 { parent[i] = v // Terminate the BFS, if we reach to sink if i == sink { return parent } marked[i] = true queue = append(queue, i) } } } // source and sink are not in the same connected component return nil } func EdmondKarp(graph WeightedGraph, source int, sink int) float64 { // Check graph emptiness if len(graph) == 0 { return 0.0 } // Check correct dimensions of the graph slice for i := 0; i < len(graph); i++ { if len(graph[i]) != len(graph) { return 0.0 } } rGraph := make(WeightedGraph, len(graph)) for i := 0; i < len(graph); i++ { rGraph[i] = make([]float64, len(graph)) } // Init the residual graph with the same capacities as the original graph copy(rGraph, graph) maxFlow := 0.0 for { parent := FindPath(rGraph, source, sink) if parent == nil { break } // Finding the max flow over the path returned by BFS // i.e. finding minimum residual capacity amonth the path edges pathFlow := math.MaxFloat64 for v := sink; v != source; v = parent[v] { u := parent[v] if rGraph[u][v] < pathFlow { pathFlow = rGraph[u][v] } } // update residual capacities of the edges and // reverse edges along the path for v := sink; v != source; v = parent[v] { u := parent[v] rGraph[u][v] -= pathFlow rGraph[v][u] += pathFlow } // Update the total flow found so far maxFlow += pathFlow } return maxFlow }