|
1 | 1 | using System;
|
2 |
| -using System.Collections.Generic; |
3 |
| -using System.Linq; |
4 |
| -using System.Text; |
5 | 2 |
|
6 |
| -// Algorithm from: http://tawani.blogspot.com/2009/02/topological-sorting-and-cyclic.html |
7 | 3 | namespace NHibernate.Loader
|
8 | 4 | {
|
9 | 5 | class TopologicalSorter
|
10 | 6 | {
|
11 |
| - #region - Private Members - |
| 7 | + sealed class Node |
| 8 | + { |
| 9 | + public int Index { get; private set; } |
| 10 | + public int SuccessorCount { get; private set; } |
| 11 | + public bool Eliminated { get; private set; } |
| 12 | + System.Action _onEliminate; |
| 13 | + |
| 14 | + public Node(int index) |
| 15 | + { |
| 16 | + Index = index; |
| 17 | + SuccessorCount = 0; |
| 18 | + Eliminated = false; |
| 19 | + _onEliminate = null; |
| 20 | + } |
12 | 21 |
|
13 |
| - private readonly int[] _vertices; // list of vertices |
14 |
| - private readonly int[,] _matrix; // adjacency matrix |
15 |
| - private int _numVerts; // current number of vertices |
16 |
| - private readonly int[] _sortedArray; |
| 22 | + public void RegisterSuccessor(Node successor) |
| 23 | + { |
| 24 | + SuccessorCount++; |
| 25 | + successor._onEliminate += () => SuccessorCount--; |
| 26 | + } |
17 | 27 |
|
18 |
| - #endregion |
| 28 | + public void Eliminate() |
| 29 | + { |
| 30 | + if (_onEliminate != null) |
| 31 | + { |
| 32 | + _onEliminate(); |
| 33 | + _onEliminate = null; |
| 34 | + } |
| 35 | + Eliminated = true; |
| 36 | + } |
| 37 | + } |
19 | 38 |
|
20 |
| - #region - CTors - |
| 39 | + readonly Node[] _nodes; |
| 40 | + int _nodeCount; |
21 | 41 |
|
22 | 42 | public TopologicalSorter(int size)
|
23 | 43 | {
|
24 |
| - _vertices = new int[size]; |
25 |
| - _matrix = new int[size, size]; |
26 |
| - _numVerts = 0; |
27 |
| - for (int i = 0; i < size; i++) |
28 |
| - for (int j = 0; j < size; j++) |
29 |
| - _matrix[i, j] = 0; |
30 |
| - _sortedArray = new int[size]; // sorted vert labels |
| 44 | + _nodes = new Node[size]; |
| 45 | + _nodeCount = 0; |
31 | 46 | }
|
32 | 47 |
|
33 |
| - #endregion |
34 |
| - |
35 |
| - #region - Public Methods - |
| 48 | + /// <summary> |
| 49 | + /// Adds a new node |
| 50 | + /// </summary> |
| 51 | + /// <returns>index of the new node</returns> |
| 52 | + public int AddVertex() |
| 53 | + { |
| 54 | + // note: this method cannot add nodes beyond the initial size defined in the constructor. |
| 55 | + var node = new Node(_nodeCount++); |
| 56 | + _nodes[node.Index] = node; |
| 57 | + return node.Index; |
| 58 | + } |
36 | 59 |
|
37 |
| - public int AddVertex(int vertex) |
| 60 | + /// <summary> |
| 61 | + /// Adds an edge from the node with the given sourceNodeIndex to the node with the given destinationNodeIndex |
| 62 | + /// </summary> |
| 63 | + /// <param name="sourceNodeIndex">index of a previously added node that is the source of the edge</param> |
| 64 | + /// <param name="destinationNodeIndex">index of a previously added node the is the destination of the edge</param> |
| 65 | + public void AddEdge(int sourceNodeIndex, int destinationNodeIndex) |
38 | 66 | {
|
39 |
| - _vertices[_numVerts++] = vertex; |
40 |
| - return _numVerts - 1; |
| 67 | + // note: invalid values for "sourceNodeIndex" and "destinationNodeIndex" will either lead to an "IndexOutOfRangeException" or a "NullReferenceException" |
| 68 | + _nodes[sourceNodeIndex].RegisterSuccessor(_nodes[destinationNodeIndex]); |
41 | 69 | }
|
42 | 70 |
|
43 |
| - public void AddEdge(int start, int end) |
| 71 | + void EliminateNode(Node node) |
44 | 72 | {
|
45 |
| - _matrix[start, end] = 1; |
| 73 | + node.Eliminate(); |
| 74 | + |
| 75 | + // decrease _nodeCount whenever the last nodes are already eliminated. |
| 76 | + // This should save time for the following checks in "getNodeWithoutSuccessors". |
| 77 | + while (_nodeCount > 0 && _nodes[_nodeCount - 1].Eliminated) |
| 78 | + _nodeCount--; |
46 | 79 | }
|
47 | 80 |
|
48 |
| - public int[] Sort() // toplogical sort |
| 81 | + Node GetNodeWithoutSuccessors() |
49 | 82 | {
|
50 |
| - while (_numVerts > 0) // while vertices remain, |
| 83 | + // note: Check the nodes in reverse order, since that allows decreases of "_nodeCount" in "eliminateNode" |
| 84 | + // as often as possible because high indices are preferred over low indices whenever there is a choice. |
| 85 | + for (int i = _nodeCount - 1; i >= 0; i--) |
51 | 86 | {
|
52 |
| - // get a vertex with no successors, or -1 |
53 |
| - int currentVertex = noSuccessors(); |
54 |
| - if (currentVertex == -1) // must be a cycle |
55 |
| - throw new Exception("Graph has cycles"); |
| 87 | + var node = _nodes[i]; |
| 88 | + if (node.Eliminated) |
| 89 | + continue; |
56 | 90 |
|
57 |
| - // insert vertex label in sorted array (start at end) |
58 |
| - _sortedArray[_numVerts - 1] = _vertices[currentVertex]; |
| 91 | + if (node.SuccessorCount > 0) |
| 92 | + continue; |
59 | 93 |
|
60 |
| - deleteVertex(currentVertex); // delete vertex |
| 94 | + return node; |
61 | 95 | }
|
62 | 96 |
|
63 |
| - // vertices all gone; return sortedArray |
64 |
| - return _sortedArray; |
| 97 | + throw new Exception("Unable to find node without successors: Graph has cycles"); |
65 | 98 | }
|
66 | 99 |
|
67 |
| - #endregion |
68 |
| - |
69 |
| - #region - Private Helper Methods - |
70 |
| - |
71 |
| - // returns vert with no successors (or -1 if no such verts) |
72 |
| - private int noSuccessors() |
| 100 | + /// <summary> |
| 101 | + /// Performs the topological sort and returns an array containing the node keys in a topological order |
| 102 | + /// </summary> |
| 103 | + /// <returns>Array of node keys</returns> |
| 104 | + public int[] Sort() |
73 | 105 | {
|
74 |
| - for (int row = 0; row < _numVerts; row++) |
75 |
| - { |
76 |
| - bool isEdge = false; // edge from row to column in adjMat |
77 |
| - for (int col = 0; col < _numVerts; col++) |
78 |
| - { |
79 |
| - if (_matrix[row, col] > 0) // if edge to another, |
80 |
| - { |
81 |
| - isEdge = true; |
82 |
| - break; // this vertex has a successor try another |
83 |
| - } |
84 |
| - } |
85 |
| - if (!isEdge) // if no edges, has no successors |
86 |
| - return row; |
87 |
| - } |
88 |
| - return -1; // no |
89 |
| - } |
| 106 | + var sortedArray = new int[_nodes.Length]; |
90 | 107 |
|
91 |
| - private void deleteVertex(int delVert) |
92 |
| - { |
93 |
| - // if not last vertex, delete from vertexList |
94 |
| - if (delVert != _numVerts - 1) |
| 108 | + // fill the array back to front as we select nodes without successors. |
| 109 | + for (int i = _nodeCount - 1; i >= 0; i--) |
95 | 110 | {
|
96 |
| - for (int j = delVert; j < _numVerts - 1; j++) |
97 |
| - _vertices[j] = _vertices[j + 1]; |
98 |
| - |
99 |
| - for (int row = delVert; row < _numVerts - 1; row++) |
100 |
| - moveRowUp(row, _numVerts); |
101 |
| - |
102 |
| - for (int col = delVert; col < _numVerts - 1; col++) |
103 |
| - moveColLeft(col, _numVerts - 1); |
| 111 | + var node = GetNodeWithoutSuccessors(); |
| 112 | + sortedArray[i] = node.Index; |
| 113 | + EliminateNode(node); |
104 | 114 | }
|
105 |
| - _numVerts--; // one less vertex |
106 |
| - } |
107 | 115 |
|
108 |
| - private void moveRowUp(int row, int length) |
109 |
| - { |
110 |
| - for (int col = 0; col < length; col++) |
111 |
| - _matrix[row, col] = _matrix[row + 1, col]; |
| 116 | + return sortedArray; |
112 | 117 | }
|
113 |
| - |
114 |
| - private void moveColLeft(int col, int length) |
115 |
| - { |
116 |
| - for (int row = 0; row < length; row++) |
117 |
| - _matrix[row, col] = _matrix[row, col + 1]; |
118 |
| - } |
119 |
| - |
120 |
| - #endregion |
121 | 118 | }
|
122 | 119 | }
|
0 commit comments