diff --git a/Algorithms.Graph/Edge.Extensions.cs b/Algorithms.Graph/Edge.Extensions.cs index e24f210..376c8b2 100644 --- a/Algorithms.Graph/Edge.Extensions.cs +++ b/Algorithms.Graph/Edge.Extensions.cs @@ -1,6 +1,4 @@ using DataStructures; -using System.Collections.Generic; - namespace Algorithms.Graph { @@ -9,58 +7,25 @@ namespace Algorithms.Graph /// public static class EdgeExtensions { - public class EdgeComparer : IEqualityComparer - { - #region IEqualityComparer - /// - /// Returns a value indicating whether this instance is equal to the edge. Transposed edges will be handled as equal edge. - /// http://msdn.microsoft.com/en-us/library/bb338049(v=vs.100).aspx - /// - /// edge x - /// edge y - /// - public bool Equals(IEdge e1, IEdge e2) - { - //edge are equal - if (e1.Equals(e2) && e2.Equals(e1)) return true; - - //edges are not equal but transposed (e1: v1->v2 e2: v2->v1 ) - if ((e1.Equals(e2) && e2.Equals(e1)).Equals(false) && - (EdgeExtensions.Equals(e1, e2, true) && EdgeExtensions.Equals(e1, e2, true)).Equals(true)) return true; - - //diffrent edges - return false; - } - - public int GetHashCode(IEdge obj) - { - return obj.GetHashCode(); - } - #endregion - } /// /// Returns a value indicating whether this instance is equal to the edge. /// /// The edge to compare to this instance. - /// If the parameter is true transported edges will be handled as equal + /// If the parameter is false transported edges will be handled as equal /// True if the instance and the overgiven edge are euqa; otherwiese, false. - public static bool Equals(this IEdge e, IEdge edge, bool permute) + public static bool Equals(this IEdge e, IEdge edge, bool graphIsdirected = true) { - if (permute) + //(use == operator instead of Equals for directed graphs, + //as the overriden equals of the edge implementeation returns true for transposed edges) + if (!graphIsdirected) { - if (!e.U.Equals(edge.V)) return false; - if (!e.V.Equals(edge.U)) return false; - if (!e.Weighted.Equals(edge.Weighted)) return false; - if (!e.GetHashCode().Equals(edge.GetHashCode())) return false; - return true; + //transposed edges uses the hash of the internal guids of the vertices + if (e != null) return e.Equals(edge); + return false; } else { - if (!e.U.Equals(edge.U)) return false; - if (!e.V.Equals(edge.V)) return false; - if (!e.Weighted.Equals(edge.Weighted)) return false; - if (!e.GetHashCode().Equals(edge.GetHashCode())) return false; - return true; + return e == edge; } } } diff --git a/Algorithms.Graph/Graph.Extensions.Xml.cs b/Algorithms.Graph/Graph.Extensions.Xml.cs index a46e6ea..f8d9276 100644 --- a/Algorithms.Graph/Graph.Extensions.Xml.cs +++ b/Algorithms.Graph/Graph.Extensions.Xml.cs @@ -17,7 +17,7 @@ public static DataContractSerializerSettings GetDataContractSerializerSettings() { return GetDataContractSerializerSettings(new List()); } - public static DataContractSerializerSettings GetDataContractSerializerSettings(List knownTypes = null, DataContractResolver dataContractResolver = null) + public static DataContractSerializerSettings GetDataContractSerializerSettings(List knownTypes, DataContractResolver dataContractResolver = null) { List types = new List() { typeof(Vertex), typeof(Edge) }; if (knownTypes != null) @@ -80,6 +80,7 @@ public static void Load(this DataStructures.Graph g, String pfilename, int maxDe } public static DataStructures.Graph Load(this XElement e, Action DataContractSerializerSettingsActionInvokrer = null) { + if (e == null) throw new ArgumentNullException(nameof(e)); DataStructures.Graph g = new DataStructures.Graph(); //load graph MemoryStream memoryStream = new MemoryStream(); diff --git a/Algorithms.Graph/Graph.Extensions.cs b/Algorithms.Graph/Graph.Extensions.cs index 2fcebfa..85e17cd 100644 --- a/Algorithms.Graph/Graph.Extensions.cs +++ b/Algorithms.Graph/Graph.Extensions.cs @@ -10,6 +10,12 @@ namespace Algorithms.Graph /// public static partial class GraphExtensions { + /// + /// Determines whether the graph is directed or undirected. + /// Uses the to generate a matrix and checks whether its a symmetric + /// + /// + /// public static bool IsDirected(this DataStructures.Graph g) { //schauen ob alle vertex jeweils 2mal verbudnen sind also 1->2 und 2->1 nur dann ist es directed=false ansonsten directed=true @@ -18,7 +24,7 @@ public static bool IsDirected(this DataStructures.Graph g) //also ist der graph genau dann ungerichtet wenn die matrix symmetrisch ist //http://en.wikipedia.org/wiki/Transpose //a transportieren also a^t = a symmetrisch - int[][] matrix = g.AdjacencyList(); + int[][] matrix = g.AdjacencyMatrix(); int[][] matrixT = new int[matrix.Length][]; for (int i = 0; i < matrix.Length; i++) { @@ -66,35 +72,40 @@ public static IEdge GetOppositeEdge(this IEdge edge) } /// /// Returns all vertices from the graph - /// http://www.brpreiss.com/books/opus4/html/page551.html - /// http://www.cse.ohio-state.edu/~gurari/course/cis680/cis680Ch14.html#QQ1-46-90 /// /// Root IVertex of graph /// All reachable vertices public static IEnumerable DepthFirstTraversal(this DataStructures.Graph s) { if (s == null) throw new ArgumentNullException(nameof(s)); - List l = new List(); + IEnumerable l = Enumerable.Empty(); foreach (IVertex v in s.Vertices) - l.AddRange(DepthFirstTraversal(v, new List())); + { + var verticeList = DepthFirstTraversal(v, new List()); + l = l.Union(verticeList); + } return l; } private static List DepthFirstTraversal(this IVertex s, List visited) { //visist x visited.Add(s); - //FOR each y such that (x,y) is an IEdge DO foreach (IEdge e in s.Edges) { if (!visited.Contains(e.V)) { - visited = DepthFirstTraversal(e.V, visited); + DepthFirstTraversal(e.V, visited); } } return visited; } - public static IEnumerable DephFirstSearch(this IVertex s) + /// + /// DepthFirstSearch implemented as Stack + /// + /// + /// + public static IEnumerable DepthFirstSearchStack(this IVertex s) { List visited = new List(); @@ -105,14 +116,15 @@ public static IEnumerable DephFirstSearch(this IVertex s) while (stack.Count != 0) { IVertex v = stack.Pop(); - visited.Add(v); - foreach (IEdge e in v.Edges) + if (!visited.Contains(v)) { - if (!visited.Contains(e.V)) + visited.Add(v); + foreach (IEdge e in v.Edges) { stack.Push(e.V); } } + } return visited; @@ -126,9 +138,9 @@ public static IEnumerable DephFirstSearch(this IVertex s) /// /// Graph on which the adjacency list should be created /// - public static int[][] AdjacencyList(this DataStructures.Graph g) + public static int[][] AdjacencyMatrix(this DataStructures.Graph g) { - var vertices = DepthFirstTraversal(g).Sort().Distinct().ToArray(); + var vertices = DepthFirstTraversal(g).Sort().ToArray(); //create matrix int c = vertices.Length; int[][] m = new int[c][]; @@ -159,6 +171,7 @@ public static int[][] AdjacencyList(this DataStructures.Graph g) public static DataStructures.Graph KruskalDepthFirstSearch(this DataStructures.Graph g) { + if (g == null) throw new ArgumentNullException(nameof(g)); //works only with undircted graphs if (g.Directed.Equals(true)) throw new ArgumentException("Graph is not undirected"); @@ -167,8 +180,12 @@ public static DataStructures.Graph KruskalDepthFirstSearch(this DataStructures.G DataStructures.Graph g_ = g; List vertices = new List(); - //order IEdges by pyramiding weighted - IEdge[] IEdges = DepthFirstTraversal(g).SelectMany(a => a.Edges).OrderBy(e => e.Weighted).Distinct(new EdgeExtensions.EdgeComparer()).ToArray(); + //order IEdges by pyramiding weighted, distinct them + IEdge[] IEdges = DepthFirstTraversal(g). + SelectMany(a => a.Edges). + OrderBy(e => e.Weighted). + Distinct(). + ToArray(); //remove IEdges foreach (IVertex z in g_.DepthFirstTraversal()) { @@ -189,7 +206,7 @@ public static DataStructures.Graph KruskalDepthFirstSearch(this DataStructures.G v.AddEdge(u, e.Weighted); //check if circle - var o = DepthFirstSearch(u, u); + var o = DepthFirstSearch(u, u, false); if (o.First().U.Equals(u) && o.Last().V.Equals(u)) { @@ -212,58 +229,22 @@ public static DataStructures.Graph KruskalDepthFirstSearch(this DataStructures.G public static IEnumerable DepthFirstSearch(this IVertex start, IVertex goal = null, bool graphIsdirected = true) { if (start == null) throw new ArgumentNullException(nameof(start)); - if (graphIsdirected) - { - return DepthFirstSearchDirected(start, new List(), goal); - } - else - { - return DepthFirstSearchUndirected(start, new List(), goal); - } + return DepthFirstSearch(start, new List(), goal, graphIsdirected); } - private static IEnumerable DepthFirstSearchUndirected(IVertex current, List edges, IVertex goal) + private static IEnumerable DepthFirstSearch(IVertex current, List edges, IVertex goal, bool graphIsdirected) { foreach (IEdge e in current.Edges) { - if (!edges.Any(a => a.Equals(e))) + if (!edges.Any(a => EdgeExtensions.Equals(a, e, graphIsdirected))) { //mark edges edges.Add(e); - DepthFirstSearchUndirected(e.V, edges, goal); + DepthFirstSearch(e.V, edges, goal, graphIsdirected); } if (edges.Any() && edges.Last().V.Equals(goal)) return edges; } - return edges; } - private static IEnumerable DepthFirstSearchDirected(IVertex current, List edges, IVertex goal) - { - foreach (IEdge e in current.Edges) - { - //check if already visited IVertex - //(use == operator instead of Equals for directed graphs, - //as the overriden equals of the Edge implementeation returns true for transposed edges) - if (!edges.Any(a => a == e)) - { - //mark edges - edges.Add(e); - DepthFirstSearchDirected(e.V, edges, goal); - } - if (edges.Any() && edges.Last().V.Equals(goal)) return edges; - } - return edges; - } - - /// - /// Determinds if the overgiven is adjacent to the current IVertex - /// - /// the IVertex to check - /// True if the overgiven IVertex is adjacent - public static Boolean Adjacent(this IVertex v) - { - if (v == null) throw new ArgumentNullException(nameof(v)); - return !v.Edges.Any(a => a.U.Equals(v) || a.V.Equals(v)); - } /// /// Calculates the distance by summing the weighted of the IEdges. /// @@ -279,22 +260,5 @@ public static int Distance(this IEnumerable edges) return distance; } - - - public static object Connected(this DataStructures.Graph g, IVertex a, IVertex b) - { - // Eine Folge von karten e1,e2,...ek e E(G) eines ungerichteten G heißt katenfolge , wenn es knoten v,v1,v2,...vk1 w eV(G) mit - //% gibt ,d.h. man kann die katen e1,2,...ek,ohne absetzen durchlafeun. k... anzahl der kanten - //Tiefensuchen hier nehmen Katenfolge von a -> b zurück geben - return null; - } - //public static bool Connected(this Graph g, IVertex a, IVertex b) - //{ - // // Eine Folge von karten e1,e2,...ek e E(G) eines ungerichteten G heißt katenfolge , wenn es knoten v,v1,v2,...vk1 w eV(G) mit - // //% gibt ,d.h. man kann die katen e1,2,...ek,ohne absetzen durchlafeun. k... anzahl der kanten - // //Tiefensuchen hier nehmen Katenfolge von a -> b zurück geben - // //return g.Connected(a,b) ==null ? false : true; - // return false; - //} } } diff --git a/Algorithms.Graph/Vertex.cs b/Algorithms.Graph/Vertex.cs deleted file mode 100644 index c0ceedb..0000000 --- a/Algorithms.Graph/Vertex.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using DataStructures; - -namespace Algorithms.Graph -{ - public static partial class VertexAlgorithms - { - public static IList Depth_First_Traversal(this IVertex s, IList visited) - { - //visist x - visited.Add(s); - - //FOR each y such that (x,y) is an edge DO - foreach (IEdge e in s.Edges) - { - if (!visited.Contains(e.V)) - { - visited = Depth_First_Traversal(e.V, visited); - } - } - return visited; - } - } -} diff --git a/DataStructure.Test/Graph.Extensions.Test.cs b/DataStructure.Test/Graph.Extensions.Test.cs index 8e1934d..e07fc19 100644 --- a/DataStructure.Test/Graph.Extensions.Test.cs +++ b/DataStructure.Test/Graph.Extensions.Test.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Xml.Linq; using Algorithms.Graph; @@ -182,14 +183,14 @@ public void DepthFirstSearch_on_undirected_graph_with_tail_should_find_goal() } [Fact] - public void DephFirstSearch_find_all_vertices_from_graph() + public void DepthFirstSearchStack_find_all_vertices_from_graph() { //Arrange var dfsEdgesResultList = _g.Start.DepthFirstSearch(new Vertex(), false); var dfsEdgeVertexResultList = dfsEdgesResultList.ToVertexList(); //Act - var dfsVerticesResultList = _g.Start.DephFirstSearch().Distinct(); - + var dfsVerticesResultList = _g.Start.DepthFirstSearchStack(); + //Assert Assert.Equal(7, dfsVerticesResultList.Count()); @@ -201,11 +202,34 @@ public void DephFirstSearch_find_all_vertices_from_graph() Assert.NotNull(resultVertex); } } + [Fact] + public void DepthFirstSearchStack_on_undirected_graph_should_find_all_vertices() + { + //example which occours in kruskal + var v1 = new Vertex(1); + var v2 = new Vertex(2); + var v3 = new Vertex(3); + var v4 = new Vertex(4); + var v5 = new Vertex(5); + var v6 = new Vertex(6); + var v7 = new Vertex(7); + + v3.AddEdge(v6, 0, false); + v3.AddEdge(v4, 0, false); + v1.AddEdge(v2, 0, false); + v5.AddEdge(v6, 0, false); + v5.AddEdge(v7, 0, false); + v1.AddEdge(v4, 0, false); + v4.AddEdge(v6, 0, false); + + var vertices = v6.DepthFirstSearchStack(); + Assert.Equal(7, vertices.Count()); + } [Fact] - public void AdjacencyList_should_return_expected_result() + public void AdjacencyMatrix_should_return_expected_result() { - int[][] result = _g.AdjacencyList(); + int[][] result = _g.AdjacencyMatrix(); int[][] expected = { new int[]{0,2,5,3,0,0,0}, new int[]{2,0,4,0,6,0,0}, @@ -297,7 +321,42 @@ public void IsDirected_should_return_false_for_krusal_directed_example() Assert.True(result); } + [Fact] + public void KruskalDepthFirstSearch_should_find_path() + { + Graph resultGraph = _g.KruskalDepthFirstSearch(); + //check if all vertices are contained in the graph + var resultVertices = resultGraph.DepthFirstTraversal(); + Assert.Equal(7, resultVertices.Count()); + //check for circle + foreach (var vertex in resultVertices) + { + var resultFromVertexToVertex = vertex.DepthFirstSearch(vertex, false); + var lastEdge = resultFromVertexToVertex.Last(); + Assert.NotEqual(vertex, lastEdge.V); + } + } + [Fact] + public void DepthFirstTraversal_should_find_all_vertices() + { + var resultVertexList = _g.DepthFirstTraversal(); + Assert.Equal(7, resultVertexList.Count()); + } + [Fact] + public void Distance_on_two_edges_with_weight_3_and_5_should_return_8() + { + Vertex vertex1 = new Vertex(); + Vertex vertex2 = new Vertex(); + Vertex vertex3 = new Vertex(); + + IList edges = new List(); + edges.Add(vertex1.AddEdge(vertex2, 3, false)); + edges.Add(vertex2.AddEdge(vertex3, 5, false)); + int distance = edges.Distance(); + + Assert.Equal(8, distance); + } } } diff --git a/DataStructures/Edge.cs b/DataStructures/Edge.cs index ed5620c..21742bc 100644 --- a/DataStructures/Edge.cs +++ b/DataStructures/Edge.cs @@ -16,23 +16,22 @@ public class Edge : IEdge /// /// Initializes a new instance of the Edge class. /// - /// Vertex of the Edge - /// Vertex of the Edge - public Edge(IVertex pu, IVertex pv) + /// Vertex of the Edge + /// Vertex of the Edge + public Edge(IVertex u, IVertex v) { - _u = pu; - _v = pv; + _u = u; + _v = v; + Value = default; } /// /// Initializes a new instance of the Edge class. /// - /// Vertex of the Edge - /// Vertex of the Edge + /// Vertex of the Edge + /// Vertex of the Edge /// Sets the Weighted of the Edge - public Edge(IVertex pu, IVertex pv, int pweighted) + public Edge(IVertex u, IVertex v, int pweighted) : this(u, v) { - _u = pu; - _v = pv; _weighted = pweighted; } public TData Value { get; set; } diff --git a/DataStructures/Vertex.cs b/DataStructures/Vertex.cs index ba7ca0d..6f671d5 100644 --- a/DataStructures/Vertex.cs +++ b/DataStructures/Vertex.cs @@ -20,7 +20,11 @@ public class Vertex : IVertex /// /// Initializes a new instance of the Vertex class. /// - public Vertex() { _Guid = Guid.NewGuid(); } + public Vertex() + { + _Guid = Guid.NewGuid(); + _Data = default; + } /// /// Initializes a new instance of the Vertex class that contains the specified weighted.