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