diff --git a/SharpGraph.Tests/test/SpanningTreeTest.cs b/SharpGraph.Tests/test/SpanningTreeTest.cs index 1f52d25..11e5864 100644 --- a/SharpGraph.Tests/test/SpanningTreeTest.cs +++ b/SharpGraph.Tests/test/SpanningTreeTest.cs @@ -278,6 +278,84 @@ public void MinimumSpanningTreePrimTest2() Assert.True(span.Contains(wedge1) && span.Contains(wedge2) && !span.Contains(wedge3)); } + [Fact] + public void MinimumSpanningTreeBoruvkaTest1() + { + var nodes = NodeGenerator.GenerateNodes(8); + var nodeList = new List(nodes); + var wedge1 = new Edge(nodeList[0], nodeList[1]); + var wedge2 = new Edge(nodeList[0], nodeList[2]); + var wedge3 = new Edge(nodeList[0], nodeList[7]); + var wedge4 = new Edge(nodeList[1], nodeList[2]); + var wedge5 = new Edge(nodeList[1], nodeList[3]); + var wedge6 = new Edge(nodeList[1], nodeList[5]); + var wedge7 = new Edge(nodeList[2], nodeList[4]); + var wedge8 = new Edge(nodeList[2], nodeList[5]); + var wedge9 = new Edge(nodeList[2], nodeList[6]); + var wedge10 = new Edge(nodeList[3], nodeList[5]); + var wedge11 = new Edge(nodeList[4], nodeList[6]); + var wedge12 = new Edge(nodeList[5], nodeList[6]); + var wedge13 = new Edge(nodeList[6], nodeList[7]); + var edges = new List(); + edges.Add(wedge1); + edges.Add(wedge2); + edges.Add(wedge3); + edges.Add(wedge4); + edges.Add(wedge5); + edges.Add(wedge6); + edges.Add(wedge7); + edges.Add(wedge8); + edges.Add(wedge9); + edges.Add(wedge10); + edges.Add(wedge11); + edges.Add(wedge12); + edges.Add(wedge13); + var g = new Graph(edges); + + g.AddComponent(wedge1).Weight = 2.4f; + g.AddComponent(wedge2).Weight = 23.4f; + g.AddComponent(wedge3).Weight = 13.5f; + g.AddComponent(wedge4).Weight = 66.4f; + g.AddComponent(wedge5).Weight = 19.14f; + g.AddComponent(wedge6).Weight = 7.905f; + g.AddComponent(wedge7).Weight = 17.05f; + g.AddComponent(wedge8).Weight = 100.8f; + g.AddComponent(wedge9).Weight = 88.8f; + g.AddComponent(wedge10).Weight = 10.8f; + g.AddComponent(wedge11).Weight = 20.8f; + g.AddComponent(wedge12).Weight = 14.2f; + g.AddComponent(wedge13).Weight = 10.55f; + + var span = g.GenerateMinimumSpanningTree(SpanningTreeAlgorithm.Boruvka); + + Assert.Equal(7, span.Count); + } + + [Fact] + public void MinimumSpanningTreeBoruvkaTest2() + { + var b1 = new Node("1"); + var b2 = new Node("2"); + var b3 = new Node("3"); + var wedge1 = new Edge(b1, b2); + var wedge2 = new Edge(b1, b3); + var wedge3 = new Edge(b2, b3); + var edges = new List(); + edges.Add(wedge1); + edges.Add(wedge2); + edges.Add(wedge3); + + var g = new Graph(edges); + + g.AddComponent(wedge1).Weight = -5.1f; + g.AddComponent(wedge2).Weight = 0f; + g.AddComponent(wedge3).Weight = 3.5f; + var span = g.GenerateMinimumSpanningTree(SpanningTreeAlgorithm.Boruvka); + + // minimum spanning tree should contain only edges 1 and 2, not edge 3. + Assert.True(span.Contains(wedge1) && span.Contains(wedge2) && !span.Contains(wedge3)); + } + [Fact] public void SpanningTreeTest1() { diff --git a/SharpGraph/src/algorithms/DisjointSet.cs b/SharpGraph/src/algorithms/DisjointSet.cs index a700a74..cd02631 100644 --- a/SharpGraph/src/algorithms/DisjointSet.cs +++ b/SharpGraph/src/algorithms/DisjointSet.cs @@ -9,12 +9,22 @@ namespace SharpGraph /// /// Disjoint set. /// + // internal class DisjointSet + // { + // internal int Parent; + // internal int Rank; + // internal DisjointSet(int parent, int rank) + // { + // this.Parent = parent; + // this.Rank = rank; + // } + // } internal class DisjointSet { - internal int Parent; + internal Node Parent; internal int Rank; - internal DisjointSet(int parent, int rank) + internal DisjointSet(Node parent, int rank) { this.Parent = parent; this.Rank = rank; diff --git a/SharpGraph/src/algorithms/Graph.SpanningTree.cs b/SharpGraph/src/algorithms/Graph.SpanningTree.cs index 9593c21..9ad0a2c 100644 --- a/SharpGraph/src/algorithms/Graph.SpanningTree.cs +++ b/SharpGraph/src/algorithms/Graph.SpanningTree.cs @@ -12,6 +12,7 @@ namespace SharpGraph { public enum SpanningTreeAlgorithm { + Boruvka, Kruskal, Prim, } @@ -67,6 +68,8 @@ public List GenerateMinimumSpanningTree( switch (spanningTreeAlgorithm) { + case SpanningTreeAlgorithm.Boruvka: + return this.GenerateMinimumSpanningTreeBoruvka(); case SpanningTreeAlgorithm.Kruskal: return this.GenerateMinimumSpanningTreeKruskal(); case SpanningTreeAlgorithm.Prim: @@ -94,19 +97,22 @@ public List GenerateSpanningTree() var edges = this.GetEdges(); _ = new List(edges); var stEdges = new List(); - var ds = new List(); - var nodeIndexDict = new Dictionary(); + var ds = new Dictionary(); var nodeList = this.nodes.ToList(); - for (var i = 0; i < nodeList.Count; i++) - { - ds.Add(new DisjointSet(i, 0)); - nodeIndexDict[nodeList[i]] = i; - } foreach (var edge in edges) { - var x = this.Find(ds, nodeIndexDict[edge.From()]); - var y = this.Find(ds, nodeIndexDict[edge.To()]); + var (x, b1) = this.Find(ds, edge.From()); + var (y, b2) = this.Find(ds, edge.To()); + if (!b1) + { + ds[x] = new DisjointSet(x, 0); + } + + if (!b2) + { + ds[y] = new DisjointSet(y, 0); + } if (x != y) { @@ -118,39 +124,110 @@ public List GenerateSpanningTree() return stEdges; } - internal int Find(List subsets, int node) + internal (Node, bool) Find(Dictionary subsets, Node node) { + if (!subsets.ContainsKey(node)) + { + return (node, false); + } + if (subsets[node].Parent != node) { - subsets[node].Parent = this.Find(subsets, subsets[node].Parent); + var a = this.Find(subsets, subsets[node].Parent); + if (a.Item2) + { + subsets[node].Parent = a.Item1; + } } - return subsets[node].Parent; + return (subsets[node].Parent, true); } - internal void Union(List subsets, int a, int b) + internal void Union(Dictionary subsets, Node a, Node b) { var rootA = this.Find(subsets, a); var rootB = this.Find(subsets, b); - if (subsets[rootA].Rank < subsets[rootB].Rank) + if (subsets[rootA.Item1].Rank < subsets[rootB.Item1].Rank) { - subsets[rootA].Parent = rootB; + subsets[rootA.Item1].Parent = rootB.Item1; } else { - subsets[rootB].Parent = rootA; - if (subsets[rootB].Rank == subsets[rootA].Rank) + subsets[rootB.Item1].Parent = rootA.Item1; + if (subsets[rootB.Item1].Rank == subsets[rootA.Item1].Rank) + { + subsets[rootA.Item1].Rank++; + } + } + } + + /// + /// Generates a minimum spanning tree on the graph. The weight is defined using the. + /// EdgeWeight component's. Weight value. + /// The algorithm uses Boruvka's Algorithm. The time complexity is + /// ~O(E log N), where E is number of edges, N is number of nodes. + /// + /// Minimum spanning tree, as a list of edges. + private List GenerateMinimumSpanningTreeBoruvka() + { + var ds = new Dictionary(); + var nodeList = this.nodes.ToList(); + var stEdges = new List(); + var minimumEdges = new Dictionary(); + + var nTrees = 0; + + while (nTrees < nodeList.Count - 1) + { + foreach (var edge in this.edges) { - subsets[rootA].Rank++; + var (x, b1) = this.Find(ds, edge.From()); + var (y, b2) = this.Find(ds, edge.To()); + if (!b1) + { + ds[x] = new DisjointSet(x, 0); + } + + if (!b2) + { + ds[y] = new DisjointSet(y, 0); + } + + if (x != y) + { + var edgeWeight = this.GetComponent(edge).Weight; + if ( + (minimumEdges.ContainsKey(x) && edgeWeight <= minimumEdges[x]) + || !minimumEdges.ContainsKey(x) + ) + { + minimumEdges[x] = edgeWeight; + } + + if ( + (minimumEdges.ContainsKey(y) && edgeWeight <= minimumEdges[y]) + || !minimumEdges.ContainsKey(y) + ) + { + minimumEdges[y] = edgeWeight; + } + + stEdges.Add(edge); + nTrees++; + this.Union(ds, x, y); + } } } + + return stEdges; } /// /// Generates a minimum spanning tree on the graph. The weight is defined using the. /// EdgeWeight component's. Weight value. - /// The algorithm uses Kruskal's Algorithm. + /// The algorithm uses Kruskal's Algorithm. The time complexity is + /// ~O(E log N), where E is number of edges, N is number of nodes. /// /// Minimum spanning tree, as a list of edges. private List GenerateMinimumSpanningTreeKruskal() @@ -163,21 +240,22 @@ private List GenerateMinimumSpanningTreeKruskal() .Weight.CompareTo(this.GetComponent(y).Weight); } ); - var gcopy = new List(edges); var stEdges = new List(); - var ds = new List(); - var nodeIndexDict = new Dictionary(); - var nodeList = this.nodes.ToList(); - for (var i = 0; i < nodeList.Count; i++) - { - ds.Add(new DisjointSet(i, 0)); - nodeIndexDict[nodeList[i]] = i; - } + var ds = new Dictionary(); foreach (var edge in edges) { - var x = this.Find(ds, nodeIndexDict[edge.From()]); - var y = this.Find(ds, nodeIndexDict[edge.To()]); + var (x, b1) = this.Find(ds, edge.From()); + var (y, b2) = this.Find(ds, edge.To()); + if (!b1) + { + ds[x] = new DisjointSet(x, 0); + } + + if (!b2) + { + ds[y] = new DisjointSet(y, 0); + } if (x != y) { @@ -192,7 +270,8 @@ private List GenerateMinimumSpanningTreeKruskal() /// /// Generates a minimum spanning tree on the graph. The weight is defined using the. /// EdgeWeight component's. Weight value. - /// The algorithm uses Prim's Algorithm. + /// The algorithm uses Prim's Algorithm. The time complexity is + /// ~O(E log N), where E is number of edges, N is number of nodes. /// /// Minimum spanning tree, as a list of edges. private List GenerateMinimumSpanningTreePrim()