Skip to content

Commit 4bda4a3

Browse files
committed
Reimplement sort using depth-first-search
1 parent 9088f36 commit 4bda4a3

File tree

2 files changed

+117
-2
lines changed
  • lib/node_modules/@stdlib/utils/compact-adjacency-matrix

2 files changed

+117
-2
lines changed

lib/node_modules/@stdlib/utils/compact-adjacency-matrix/examples/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,4 @@ console.log( adj.toAdjacencyList() );
4949

5050
// Compute a topological ordering:
5151
console.log( adj.toposort() );
52-
// => [ 1, 0, 2, 3 ]
52+
// => [ [ 1, 0, 2, 3 ], null ]

lib/node_modules/@stdlib/utils/compact-adjacency-matrix/lib/main.js

+116-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ var Int32Array = require( '@stdlib/array/int32' );
2828
var Int8Array = require( '@stdlib/array/int8' );
2929
var ceil = require( '@stdlib/math/base/special/ceil' );
3030
var floor = require( '@stdlib/math/base/special/floor' );
31+
var grev = require( '@stdlib/blas/ext/base/grev' );
3132
var fifo = require( '@stdlib/utils/fifo' );
3233
var setBit = require( './set_bit.js' );
3334
var clearBit = require( './clear_bit.js' );
@@ -579,7 +580,7 @@ setReadOnly( CompactAdjacencyMatrix.prototype, 'toAdjacencyList', function toAdj
579580
* var order = adj.toposort();
580581
* // returns [ 1, 0, 2, 3 ]
581582
*/
582-
setReadOnly( CompactAdjacencyMatrix.prototype, 'toposort', function toposort() {
583+
setReadOnly( CompactAdjacencyMatrix.prototype, 'toposort_bfs', function toposort() {
583584
var visited;
584585
var degs;
585586
var buf;
@@ -660,6 +661,120 @@ setReadOnly( CompactAdjacencyMatrix.prototype, 'toposort', function toposort() {
660661
return out;
661662
});
662663

664+
/**
665+
* Returns a topological ordering of the directed graph.
666+
*
667+
* ## Notes
668+
*
669+
* - The function returns a two-element array.
670+
* - If the function is able to compute a topological ordering, the first array element is the topological ordering and the second element is `null`.
671+
* - If a topological ordering cannot be achieved (e.g., due to the graph not being a directed acyclic graph (DAG)), the first array element is `null` and the second element is the first encountered cycle.
672+
*
673+
* @name toposort
674+
* @type Function
675+
* @memberof CompactAdjacencyMatrix.prototype
676+
* @returns {Array} topological ordering
677+
*
678+
* @example
679+
* var adj = new CompactAdjacencyMatrix( 4 );
680+
* // returns <CompactAdjacencyMatrix>
681+
*
682+
* adj.addEdge( 1, 0 );
683+
* adj.addEdge( 1, 2 );
684+
* adj.addEdge( 0, 2 );
685+
* adj.addEdge( 2, 3 );
686+
*
687+
* var results = adj.toposort();
688+
* // returns <Array>
689+
*
690+
* var order = results[ 0 ];
691+
* // returns [ 1, 0, 2, 3 ]
692+
*
693+
* var cycle = results[ 1 ];
694+
* // returns null
695+
*/
696+
setReadOnly( CompactAdjacencyMatrix.prototype, 'toposort', function toposort() {
697+
var marks;
698+
var self;
699+
var out;
700+
var idx;
701+
var err;
702+
var N;
703+
var s;
704+
var i;
705+
706+
self = this;
707+
N = this._N;
708+
709+
// Initialize an empty list that will contain the sorted vertices:
710+
out = [];
711+
712+
// If the graph is empty, nothing to sort...
713+
if ( this._N === 0 ) {
714+
return [ out, null ];
715+
}
716+
// Initialize an array for keeping track of whether a vertex has been "visited":
717+
marks = new Int8Array( N );
718+
719+
// Initialize a stack for keeping track of cycles:
720+
s = [];
721+
722+
// Process vertices using depth-first-search...
723+
idx = [ 0, 0 ];
724+
for ( i = 0; i < N; i++ ) {
725+
if ( marks[ i ] === 0 ) {
726+
err = visit( i );
727+
if ( err !== 0 ) {
728+
// Found a cycle...
729+
return [ null, s ];
730+
}
731+
}
732+
}
733+
grev( out.length, out, 1 );
734+
return [ out, null ];
735+
736+
/**
737+
* Visits a graph vertex and follows edges until finding a leaf vertex (if one exists).
738+
*
739+
* ## Notes
740+
*
741+
* - If the function is able to successfully perform a depth-first-search, the functions returns `0`; otherwise, the function returns `-1` in the event of a cycle.
742+
*
743+
* @private
744+
* @param {NonNegativeInteger} i - vertex
745+
* @returns {integer} error code
746+
*/
747+
function visit( i ) {
748+
var err;
749+
var j;
750+
if ( marks[ i ] === 2 ) {
751+
return 0;
752+
}
753+
if ( marks[ i ] === 1 ) {
754+
return -1;
755+
}
756+
marks[ i ] = 1;
757+
for ( j = 0; j < N; j++ ) {
758+
// Follow all edges from the current vertex:
759+
idx = self._loc( i, j, idx ); // eslint-disable-line no-underscore-dangle
760+
if ( isSet( self._buffer[ idx[0] ], idx[1] ) ) { // eslint-disable-line no-underscore-dangle
761+
err = visit( j );
762+
if ( err !== 0 ) {
763+
s.push( j );
764+
return err;
765+
}
766+
}
767+
}
768+
marks[ i ] = 2;
769+
out.push( i );
770+
return 0;
771+
}
772+
});
773+
774+
// TODO: add static fromAdjacencyList( adjlist ), fromEdges( N, edges ) methods
775+
776+
// TODO: consider supporting a partial sort for toposort (i.e., first N, or last N (-N), or range M to N)
777+
663778

664779
// EXPORTS //
665780

0 commit comments

Comments
 (0)