Graph.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Text;
  5. namespace Eppstein
  6. {
  7. #region HelperClasses
  8. /// <summary>
  9. /// Class for nodes in shortest path tree, with comparing capability
  10. /// </summary>
  11. public class SP_Node : IComparable
  12. {
  13. /// <summary>
  14. /// Contained edge
  15. /// </summary>
  16. public Edge Edge;
  17. /// <summary>
  18. /// Weight of node (edge)
  19. /// </summary>
  20. public int Weight;
  21. /// <summary>
  22. /// Public constructor
  23. /// </summary>
  24. /// <param name="_edge">Edge object</param>
  25. /// <param name="_weight">Weight of node</param>
  26. public SP_Node(Edge _edge, int _weight)
  27. {
  28. this.Edge = _edge;
  29. this.Weight = _weight;
  30. }
  31. /// <summary>
  32. /// Implements IComparable's CompareTo method
  33. /// Compare two SP_Node objects by it weight values
  34. /// </summary>
  35. /// <param name="_obj">Object to compare with</param>
  36. /// <returns>-1 if this is shorter than object, 1 if countersense, 0 if both are equal</returns>
  37. /// <exception cref="System.Exception">Thrown when obj is not SP_Node type</exception>
  38. int IComparable.CompareTo(object _obj)
  39. {
  40. return Math.Sign(this.Weight - ((SP_Node)_obj).Weight);
  41. }
  42. /// <summary>
  43. /// Returns node's descriptive string
  44. /// </summary>
  45. /// <returns>Edge and weight string</returns>
  46. public override string ToString()
  47. {
  48. return /*this.Edge+*/" ["+Weight+"]";
  49. }
  50. }
  51. /// <summary>
  52. /// Sidetracks collection, with comparing capability
  53. /// </summary>
  54. public class ST_Node : IComparable
  55. {
  56. /// <summary>
  57. /// Collection of edges (sidetracks), not exactly a full path
  58. /// </summary>
  59. public Path Sidetracks;
  60. /// <summary>
  61. /// Weight of node (sum of sidetracks)
  62. /// </summary>
  63. public int Weight;
  64. /// <summary>
  65. /// Public constructor
  66. /// </summary>
  67. /// <param name="_sidetracks">Sidetrack collection</param>
  68. /// <remarks>Weight is calculated using path's weight</remarks>
  69. public ST_Node(Path _sidetracks)
  70. {
  71. this.Sidetracks = _sidetracks;
  72. this.Weight = _sidetracks.DeltaWeight;
  73. }
  74. /// <summary>
  75. /// Implements IComparable's CompareTo method
  76. /// Compare two ST_Node objects by it weight values
  77. /// </summary>
  78. /// <param name="_obj">Object to compare with</param>
  79. /// <returns>-1 if this is shorter than object, 1 if countersense, 0 if both are equal</returns>
  80. /// <exception cref="System.Exception">Thrown when obj is not ST_Node type</exception>
  81. int IComparable.CompareTo(object _obj)
  82. {
  83. return Math.Sign(this.Weight - ((ST_Node)_obj).Weight);
  84. }
  85. /// <summary>
  86. /// Returns node's descriptive string
  87. /// </summary>
  88. /// <returns>Weight as string</returns>
  89. public override string ToString()
  90. {
  91. return /*this.Sidetracks+*/" ["+Weight+"]";
  92. }
  93. }
  94. #endregion
  95. /// <summary>
  96. /// Contains a directed graph, composed by vertices and directed edges collections
  97. /// </summary>
  98. public class Graph
  99. {
  100. #region Private fields
  101. /// <summary>
  102. /// Linear array of vertices
  103. /// </summary>
  104. private List<Vertex> Vertices;
  105. /// <summary>
  106. /// Collections of Sidetracks in graph, ordered by total delta
  107. /// </summary>
  108. private PriorityQueue<ST_Node> PathsHeap;
  109. /// <summary>
  110. /// Flag to indicate if shortest paths have been recalculated
  111. /// </summary>
  112. private bool Ready;
  113. /// <summary>
  114. /// Source vertex, is valid after main shortest path calculation
  115. /// </summary>
  116. private Vertex SourceVertex;
  117. /// <summary>
  118. /// Endpoint vertex, is valid after main shortest path calculation
  119. /// </summary>
  120. private Vertex EndVertex;
  121. #endregion
  122. #region Public methods
  123. /// <summary>
  124. /// Default constructor
  125. /// </summary>
  126. public Graph()
  127. {
  128. Vertices = new List<Vertex>();
  129. Ready = false;
  130. }
  131. /// <summary>
  132. /// Creates all vertices in graph by parsing a string, remove all previous vertices and edges on lists
  133. /// </summary>
  134. /// <param name="_vertices">String containing a comma-separated list with all vertex labels</param>
  135. /// <returns>True if all labels well parsed, false if not</returns>
  136. /// <remarks>A label must start with a letter and have all remain characters letters or digits</remarks>
  137. /// <remarks>Repeated and empty labels with be ignored</remarks>
  138. public bool CreateVertices(string _vertices)
  139. {
  140. // Scans vertices' string, creates objects, and add to vertices' list
  141. Ready = false; // must rebuild trees
  142. Vertices.Clear(); // Remove all previous vertices
  143. string[] vertexList = _vertices.ToUpper().Split(new char[] { ',' });
  144. foreach (string _label in vertexList)
  145. {
  146. if (_label.Length == 0) // ignore empty labels
  147. continue;
  148. if (!char.IsLetter(_label, 0)) // label must start with a letter
  149. return false;
  150. foreach (char c in _label)
  151. {
  152. if (!char.IsLetterOrDigit(c)) // label just can contain letters and digits
  153. return false;
  154. }
  155. if (GetVertex(_label)==null)
  156. Vertices.Add(new Vertex(_label));
  157. }
  158. return true;
  159. }
  160. /// <summary>
  161. /// Creates directional edges
  162. /// </summary>
  163. /// <param name="_tails">Comma-separated list of vertices for tails</param>
  164. /// <param name="_heads">Comma-separated list of vertices for heads</param>
  165. /// <param name="_weight">Weight for all edges</param>
  166. /// <param name="_group">Group name for all edges</param>
  167. /// <returns>True if vertices are valid</returns>
  168. /// <remarks>Count of tails must match count of heads, all vertices must exist</remarks>
  169. public bool CreateEdges(string _tails, string _heads, int _weight, string _group)
  170. {
  171. List<Vertex> tails = this.ParseVertices(_tails.ToUpper());
  172. List<Vertex> heads = this.ParseVertices(_heads.ToUpper());
  173. string group = _group.ToUpper();
  174. if (tails==null || heads==null)
  175. return false;
  176. if (tails.Count != heads.Count)
  177. return false;
  178. for (int i = 0; i < tails.Count; i++)
  179. {
  180. Edge e = new Edge(tails[i], heads[i], _weight, group);
  181. tails[i].RelatedEdges.Add(e);
  182. if (tails[i] != heads[i]) // Avoids duplicated reference for self-pointing edges
  183. heads[i].RelatedEdges.Add(e);
  184. }
  185. Ready = false;
  186. return true;
  187. }
  188. /// <summary>
  189. /// Change group weights
  190. /// </summary>
  191. /// <param name="_group">Group name of edges to be affected</param>
  192. /// <param name="_weight">New weights</param>
  193. public void EdgeGroupWeights(string _group, int _weight)
  194. {
  195. string group = _group.ToUpper();
  196. foreach (Vertex _v in Vertices)
  197. foreach (Edge _e in _v.RelatedEdges)
  198. if (_e.Group == group)
  199. _e.Weight = _weight;
  200. this.Ready = false;
  201. }
  202. /// <summary>
  203. /// Calculates all shortest paths between s and t and returns the first one
  204. /// </summary>
  205. /// <param name="_s">Label of initial vertex</param>
  206. /// <param name="_t">Label of ending vertex</param>
  207. /// <returns>Directional path if found, empty path if not or labels not valid</returns>
  208. public Path FindShortestPath(string _s, string _t)
  209. {
  210. Ready = false;
  211. // Parse start and end vertex strings
  212. this.SourceVertex = this.GetVertex(_s.ToUpper());
  213. this.EndVertex = this.GetVertex(_t.ToUpper());
  214. if (SourceVertex == null || EndVertex == null)
  215. return new Path(); // Invalid path
  216. // Builds shortest path tree for all vertices to t, according to Dijkstra,
  217. // storing distance to endpoint information on vertices, as described in Eppstein
  218. BuildShortestPathTree();
  219. // Fills a heap with all possible tracks from s to t, as described in Eppstein
  220. // Paths are defined uniquely by sidetrack collections (edges not in shortest paths)
  221. BuildSidetracksHeap();
  222. // Flag to indicate that shortest paths have been calculated
  223. Ready = true;
  224. return FindNextShortestPath();
  225. }
  226. /// <summary>
  227. /// Recover next pre-calculated shortest path
  228. /// </summary>
  229. /// <returns>Directional path if available, empty path if not remaining paths</returns>
  230. public Path FindNextShortestPath()
  231. {
  232. if (!Ready)
  233. return new Path(); // Invalid path
  234. // Pick next track from heap, it is ordered from shortest path to longest
  235. ST_Node node = this.PathsHeap.Dequeue();
  236. if (node == null)
  237. return new Path(); // Invalid path
  238. // Returns path reconstructed from sidetracks
  239. return RebuildPath(node.Sidetracks);
  240. }
  241. #endregion
  242. #region Private methods
  243. /// <summary>
  244. /// Parses a comma-separated list of vertices
  245. /// </summary>
  246. /// <param name="_labels">Comma-separated list of vertex labels</param>
  247. /// <returns>List of vertices, null if some vertex does not exist</returns>
  248. private List<Vertex> ParseVertices(string _labels)
  249. {
  250. string[] vertexList = _labels.ToUpper().Split(new char[] { ',' });
  251. if (vertexList.Length == 0)
  252. return null;
  253. List<Vertex> result = new List<Vertex>();
  254. foreach (string _label in vertexList)
  255. {
  256. Vertex v = this.GetVertex(_label); // Check if label exists
  257. if (v==null)
  258. return null;
  259. result.Add(v);
  260. }
  261. return result;
  262. }
  263. /// <summary>
  264. /// Returns a reference for existing vertex
  265. /// </summary>
  266. /// <param name="_label">Label of vertex to search for</param>
  267. /// <returns>Vertex reference, null if not fount or empty label</returns>
  268. private Vertex GetVertex(string _label)
  269. {
  270. if (string.IsNullOrEmpty(_label))
  271. return null;
  272. foreach (Vertex _v in Vertices)
  273. {
  274. if (_v.Equals(_label))
  275. return _v;
  276. }
  277. return null;
  278. }
  279. /// <summary>
  280. /// Clears pointers to next edge in shortest path for all vertices
  281. /// Clears distances to endpoint in shortet path vor all vertices
  282. /// </summary>
  283. private void ResetGraphState()
  284. {
  285. foreach (Vertex _v in this.Vertices)
  286. {
  287. _v.EdgeToPath = null;
  288. _v.Distance = int.MinValue;
  289. }
  290. }
  291. /// <summary>
  292. /// Builds the shortest path tree using a priority queue for given vertex
  293. /// </summary>
  294. /// <remarks>Negative edges are ignored</remarks>
  295. private void BuildShortestPathTree()
  296. {
  297. ResetGraphState(); // Reset all distances to endpoint and previous shortest path
  298. Vertex v = this.EndVertex;
  299. v.Distance = 0; // Set distance to 0 for endpoint vertex
  300. // Creates a fringe (queue) for storing edge pending to be processed
  301. PriorityQueue<SP_Node> fringe = new PriorityQueue<SP_Node>(this.Vertices.Count);
  302. // Main loop
  303. do
  304. {
  305. if (v!=null)
  306. foreach (Edge _e in v.RelatedEdges) // Evaluate all incoming edges
  307. if (_e.Head==v && _e.Weight>=0) // Ignore negative edges
  308. fringe.Enqueue(new SP_Node(_e, _e.Weight + _e.Head.Distance));
  309. SP_Node node = fringe.Dequeue(); // Extracts next element in queue
  310. if (node == null) // No pending edges to evaluate, finished
  311. break;
  312. Edge e = node.Edge;
  313. v = e.Tail;
  314. if (v.Distance == int.MinValue) // Vertex distance to endpoint not calculated yet
  315. {
  316. v.Distance = e.Weight + e.Head.Distance;
  317. v.EdgeToPath = e;
  318. }
  319. else
  320. v = null;
  321. } while (true);
  322. }
  323. /// <summary>
  324. /// Creates all posible paths by describing only the sidetracks for each path
  325. /// </summary>
  326. /// <returns></returns>
  327. private void BuildSidetracksHeap()
  328. {
  329. this.PathsHeap = new PriorityQueue<ST_Node>(Vertices.Count);
  330. Path empty = new Path();
  331. this.PathsHeap.Enqueue(new ST_Node(empty));
  332. AddSidetracks(empty, this.SourceVertex);
  333. }
  334. /// <summary>
  335. /// Adds sidetracks recursively for specified vertex and new vertices in shortest path
  336. /// </summary>
  337. /// <param name="_p">Previous sidetrack collection</param>
  338. /// <param name="_v">Vertex to evalueate</param>
  339. private void AddSidetracks(Path _p, Vertex _v)
  340. {
  341. foreach (Edge _e in _v.RelatedEdges)
  342. {
  343. if (_e.IsSidetrackOf(_v) && (_e.Head.EdgeToPath != null || _e.Head == this.EndVertex))
  344. {
  345. Path p = new Path();
  346. p.AddRange(_p);
  347. p.Add(_e);
  348. this.PathsHeap.Enqueue(new ST_Node(p));
  349. if (_e.Head != _v) // This avoids infinite cycling
  350. AddSidetracks(p, _e.Head);
  351. }
  352. }
  353. if (_v.Next != null)
  354. AddSidetracks(_p, _v.Next);
  355. }
  356. /// <summary>
  357. /// Reconstructs path from sidetracks
  358. /// </summary>
  359. /// <param name="_sidetracks">Sidetracks collections for this path, could be empty for shortest</param>
  360. /// <returns>Full path reconstructed from s to t, crossing sidetracks</returns>
  361. private Path RebuildPath(Path _sidetracks)
  362. {
  363. Path path = new Path();
  364. Vertex v = this.SourceVertex;
  365. int i = 0;
  366. // Start from s, following shortest path or sidetracks
  367. while (v != null)
  368. {
  369. // if current vertex is conected to next sidetrack, cross it
  370. if (i < _sidetracks.Count && _sidetracks[i].Tail == v)
  371. {
  372. path.Add(_sidetracks[i]);
  373. v = _sidetracks[i++].Head;
  374. }
  375. else // else continue walking on shortest path
  376. {
  377. if (v.EdgeToPath == null)
  378. break;
  379. path.Add(v.EdgeToPath);
  380. v = v.Next;
  381. }
  382. }
  383. return path;
  384. }
  385. #endregion
  386. }
  387. }