1
0
Fork 0

Enhanced pathTo for directed and labelled graphs

The `pathTo` method accepts an options hash.
Currently supported:

* direction: "both", "outbound", "inbound"
* labels: undefined or Array of Strings
This commit is contained in:
Lucas Dohmen 2012-06-13 11:55:26 +02:00
parent 70ae8d84d4
commit f11acff837
2 changed files with 109 additions and 21 deletions

View File

@ -80,6 +80,18 @@ Array.prototype.pushIfNotFound = function (element) {
}
};
////////////////////////////////////////////////////////////////////////////////
/// @brief add all elements of the other array, that are not already in here
////////////////////////////////////////////////////////////////////////////////
Array.prototype.merge = function (other_array) {
var i;
for (i = 0; i < other_array.length; i += 1) {
this.pushIfNotFound(other_array[i]);
}
};
////////////////////////////////////////////////////////////////////////////////
/// @brief shallow copy properties
////////////////////////////////////////////////////////////////////////////////
@ -773,25 +785,23 @@ Vertex.prototype.setProperty = function (name, value) {
////////////////////////////////////////////////////////////////////////////////
/// @brief find the shortest path to a certain vertex, return the ID
///
/// @FUN{@FA{vertex}.pathTo(@FA{target_vertex})}
/// @FUN{@FA{vertex}.pathTo(@FA{target_vertex}, @FA{options})}
///
////////////////////////////////////////////////////////////////////////////////
Vertex.prototype.pathTo = function (target_vertex) {
var predecessors = {}; // { ID => [ID] }
predecessors = target_vertex.determinePredecessors(this.getId());
Vertex.prototype.pathTo = function (target_vertex, options) {
var predecessors = target_vertex.determinePredecessors(this.getId(), options || {});
return target_vertex.pathesForTree(predecessors);
};
////////////////////////////////////////////////////////////////////////////////
/// @brief Helper function for pathTo
/// @brief determine all the pathes to this node from source id
///
/// @FUN{@FA{vertex}.determinePredecessors(@FA{source_id})}
/// @FUN{@FA{vertex}.determinePredecessors(@FA{source_id}, @FA{options})}
///
////////////////////////////////////////////////////////////////////////////////
Vertex.prototype.determinePredecessors = function (source_id) {
Vertex.prototype.determinePredecessors = function (source_id, options) {
var determined_list = [], // [ID]
predecessors = {}, // { ID => [ID] }
todo_list = [source_id], // [ID]
@ -803,43 +813,54 @@ Vertex.prototype.determinePredecessors = function (source_id) {
while (todo_list.length > 0) {
current_vertex_id = this._getShortestDistance(todo_list, distances);
current_vertex = this._graph.getVertex(current_vertex_id);
if (current_vertex_id === this.getId()) {
break;
} else {
todo_list.removeLastOccurrenceOf(current_vertex_id);
determined_list.push(current_vertex_id);
current_vertex = this._graph.getVertex(current_vertex_id);
current_vertex.processNeighbors(
todo_list.merge(current_vertex._processNeighbors(
determined_list,
todo_list,
distances,
predecessors
);
predecessors,
options
));
}
}
return predecessors;
};
Vertex.prototype.processNeighbors = function (determined_list, todo_list, distances, predecessors) {
////////////////////////////////////////////////////////////////////////////////
/// @brief Helper function for determinePredecessors (changes distance and predecessors
///
/// @FUN{@FA{vertex}._processNeighbors(@FA{determinedt}, @FA{distances}, @FA{predecessors})}
///
////////////////////////////////////////////////////////////////////////////////
Vertex.prototype._processNeighbors = function (determined_list, distances, predecessors, options) {
var i,
current_neighbor_id,
current_distance,
raw_neighborlist,
compared_distance;
compared_distance,
not_determined_neighbors = [];
// FIXME: Choose the neighbors according to the graph (directed/undirected)
raw_neighborlist = this.getNeighbors('both');
// FIXME: Choose the neighbors according to labels
raw_neighborlist = this.getNeighbors(options.direction || 'both', options.labels);
// console.log("Processing Neighbors for: " + this.getId());
// console.log(raw_neighborlist);
for (i = 0; i < raw_neighborlist.length; i += 1) {
current_neighbor_id = raw_neighborlist[i];
if (determined_list.lastIndexOf(current_neighbor_id) === -1) {
// FIXME: Take the weight if it is a weighted graph
// FIXME: Weights other than 1
current_distance = distances[this.getId()] + 1;
todo_list.pushIfNotFound(current_neighbor_id);
not_determined_neighbors.push(current_neighbor_id);
compared_distance = distances[current_neighbor_id];
if ((compared_distance === undefined) || (compared_distance > current_distance)) {
@ -850,6 +871,8 @@ Vertex.prototype.processNeighbors = function (determined_list, todo_list, distan
}
}
}
return not_determined_neighbors;
};
////////////////////////////////////////////////////////////////////////////////

View File

@ -152,7 +152,7 @@ function dijkstraSuite() {
graph.addEdge(v1, v2, 8, 'a');
graph.addEdge(v1, v3, 9, 'b');
result_array = v1.getNeighbors('both', ['a']);
result_array = v1.getNeighbors('both', ['a', 'c']);
assertEqual(result_array.length, 1);
assertEqual(result_array[0], 2);
@ -218,7 +218,72 @@ function dijkstraSuite() {
assertEqual(path[0].toString(), v1.getId());
assertEqual(path[1].toString(), v2.getId());
assertEqual(path[2].toString(), v3.getId());
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief get a short, distinct path on a directed graph
////////////////////////////////////////////////////////////////////////////////
testGetADirectedDistinctPath : function () {
var v1 = graph.addVertex(1),
v2 = graph.addVertex(2),
v3 = graph.addVertex(3),
v4 = graph.addVertex(4),
e1 = graph.addEdge(v1, v2),
e2 = graph.addEdge(v2, v3),
e3 = graph.addEdge(v3, v4),
e4 = graph.addEdge(v4, v1),
pathes = v1.pathTo(v3, {direction: "outbound"});
assertEqual(pathes.length, 1);
assertEqual(pathes[0][0].toString(), v1.getId());
assertEqual(pathes[0][1].toString(), v2.getId());
assertEqual(pathes[0][2].toString(), v3.getId());
},
////////////////////////////////////////////////////////////////////////////////
/// @brief get a short, distinct path on a directed graph
////////////////////////////////////////////////////////////////////////////////
testGetADirectedLabeledPath : function () {
var v1 = graph.addVertex(1),
v2 = graph.addVertex(2),
v3 = graph.addVertex(3),
v4 = graph.addVertex(4),
e1 = graph.addEdge(v1, v2, 5, "no"),
e2 = graph.addEdge(v1, v3, 6, "yes"),
e3 = graph.addEdge(v3, v4, 7, "yeah"),
e4 = graph.addEdge(v4, v2, 8, "yes"),
pathes = v1.pathTo(v2, { labels: ["yes", "yeah"] });
assertEqual(pathes.length, 1);
assertEqual(pathes[0][0].toString(), v1.getId());
assertEqual(pathes[0][1].toString(), v3.getId());
assertEqual(pathes[0][2].toString(), v4.getId());
assertEqual(pathes[0][3].toString(), v2.getId());
},
////////////////////////////////////////////////////////////////////////////////
/// @brief get a short, distinct path on a weighted graph
////////////////////////////////////////////////////////////////////////////////
/*testGetADirectedWeightedPath : function () {
var v1 = graph.addVertex(1),
v2 = graph.addVertex(2),
v3 = graph.addVertex(3),
v4 = graph.addVertex(4),
e1 = graph.addEdge(v1, v2, 5, "5", { my_weight: 5 }),
e2 = graph.addEdge(v1, v3, 6, "6", { my_weight: 1 }),
e3 = graph.addEdge(v3, v4, 7, "7", { my_weight: 1 }),
e4 = graph.addEdge(v4, v1, 8, "8", { my_weight: 1 }),
pathes = v1.pathTo(v2, { weight: "my_weight" });
assertEqual(pathes.length, 1);
assertEqual(pathes[0][0].toString(), v1.getId());
assertEqual(pathes[0][1].toString(), v3.getId());
assertEqual(pathes[0][2].toString(), v4.getId());
assertEqual(pathes[0][3].toString(), v2.getId());
}*/
};
}