JavaScript 中的最短路径
Shortest path in JavaScript
我已经搜索了数周的时间来寻找计算 JavaScript 中最短路径的方法。我一直在玩 Data Structures and Algorithms 的书 https://github.com/loiane/javascript-datastructures-algorithms/tree/master/chapter09。
我不断发现的问题是代码太过定制以至于几乎不可能重写以产生所需的结果。我希望能够获得从任何给定顶点到任何其他顶点的最短路径,而不是像 Groner 编码的那样,只是 A 的所有内容的列表。例如,我希望能够获得来自F 到 B,或从 C 到 A。
完整代码在这里:http://jsfiddle.net/8cn7e2x8/
有人能帮忙吗?
var graph = new Graph();
var myVertices = ['A','B','C','D','E','F'];
for (var i=0; i<myVertices.length; i++) {
graph.addVertex(myVertices[i]);
}
graph.addEdge('A', 'B');
graph.addEdge('B', 'C');
graph.addEdge('B', 'E');
graph.addEdge('C', 'D');
graph.addEdge('C', 'E');
graph.addEdge('C', 'G');
graph.addEdge('D', 'E');
graph.addEdge('E', 'F');
graph.dfs();
console.log('********* sortest path - BFS ***********');
var shortestPathA = graph.BFS(myVertices[0]);
//from A to all other vertices
var fromVertex = myVertices[0];
for (i = 1; i < myVertices.length; i++) {
var toVertex = myVertices[i],
path = new Stack();
for (var v = toVertex; v !== fromVertex; v = shortestPathA.predecessors[v]) {
path.push(v);
}
path.push(fromVertex);
var s = path.pop();
while (!path.isEmpty()) {
s += ' - ' + path.pop();
}
console.log(s);
}
阅读您的问题,我可以通过以下两种方式之一阅读它...
要么你试图减少它检查的东西的数量,要么你试图让自己传入变量来改变端点。我将假设前者并让其他人处理后一种情况。
粗略浏览一下这个问题,您似乎遇到了 Comp Sci 中所谓的 "The traveling salesman problem." 这是计算机编程中的经典问题,被认为是逻辑上不可能的,并且是一个很好的问题"perfect being the enemy of the good."
的示例
经典的旅行推销员问题是这样的,"Program a way for a salesman to reach all of his destination cities on a map in the shortest time. Do so without having to check every single possible path."事实是,(尚未)发现符合逻辑的方法(如果不可能或可能,则尚待证明)。也就是说,如果它不必是最短的,而只是一条较短的路径,则可以采用许多捷径。一个例子是从头到尾计算一条线,然后推入偏差以匹配最近的顶点。另一种方法是将路径分解成三角形,这些三角形仅将每个顶点连接到下一个最接近的两个顶点,然后以相同的方式连接团块,直到所有顶点都连接起来,然后只计算这些子集的起始潜在路径。
这两个答案都不能保证给你最好的答案,但它们会提供一个很好的答案,所需的计算时间要少得多,所以你不必计算来自 A、B 和 C 的每条路径等等等等
让我们首先说明,如果图未加权,广度优先搜索 (BFS) 会计算从给定源顶点开始的最短路径。换句话说,我们将路径的长度视为路径中的边数。
下面是构建未加权图的简单方法:
function Graph() {
var neighbors = this.neighbors = {}; // Key = vertex, value = array of neighbors.
this.addEdge = function (u, v) {
if (neighbors[u] === undefined) { // Add the edge u -> v.
neighbors[u] = [];
}
neighbors[u].push(v);
if (neighbors[v] === undefined) { // Also add the edge v -> u so as
neighbors[v] = []; // to implement an undirected graph.
} // For a directed graph, delete
neighbors[v].push(u); // these four lines.
};
return this;
}
请注意,我们已经实现了一个无向图。如行内注释中所述,您可以通过从 addEdge
函数中删除四行来修改代码以构建有向图。
BFS 的这种实现在有向图上同样有效:
function bfs(graph, source) {
var queue = [ { vertex: source, count: 0 } ],
visited = { source: true },
tail = 0;
while (tail < queue.length) {
var u = queue[tail].vertex,
count = queue[tail++].count; // Pop a vertex off the queue.
print('distance from ' + source + ' to ' + u + ': ' + count);
graph.neighbors[u].forEach(function (v) {
if (!visited[v]) {
visited[v] = true;
queue.push({ vertex: v, count: count + 1 });
}
});
}
}
要找到两个给定顶点之间的最短路径并显示沿路径的顶点,我们在探索图形时必须记住每个顶点的前导:
function shortestPath(graph, source, target) {
if (source == target) { // Delete these four lines if
print(source); // you want to look for a cycle
return; // when the source is equal to
} // the target.
var queue = [ source ],
visited = { source: true },
predecessor = {},
tail = 0;
while (tail < queue.length) {
var u = queue[tail++], // Pop a vertex off the queue.
neighbors = graph.neighbors[u];
for (var i = 0; i < neighbors.length; ++i) {
var v = neighbors[i];
if (visited[v]) {
continue;
}
visited[v] = true;
if (v === target) { // Check if the path is complete.
var path = [ v ]; // If so, backtrack through the path.
while (u !== source) {
path.push(u);
u = predecessor[u];
}
path.push(u);
path.reverse();
print(path.join(' → '));
return;
}
predecessor[v] = u;
queue.push(v);
}
}
print('there is no path from ' + source + ' to ' + target);
}
以下代码片段演示了您在问题中提供的图形上的这些操作。首先,我们找到从 A
可达的所有顶点的最短路径。然后我们找到从B
到G
和从G
到A
的最短路径。
function Graph() {
var neighbors = this.neighbors = {}; // Key = vertex, value = array of neighbors.
this.addEdge = function (u, v) {
if (neighbors[u] === undefined) { // Add the edge u -> v.
neighbors[u] = [];
}
neighbors[u].push(v);
if (neighbors[v] === undefined) { // Also add the edge v -> u in order
neighbors[v] = []; // to implement an undirected graph.
} // For a directed graph, delete
neighbors[v].push(u); // these four lines.
};
return this;
}
function bfs(graph, source) {
var queue = [ { vertex: source, count: 0 } ],
visited = { source: true },
tail = 0;
while (tail < queue.length) {
var u = queue[tail].vertex,
count = queue[tail++].count; // Pop a vertex off the queue.
print('distance from ' + source + ' to ' + u + ': ' + count);
graph.neighbors[u].forEach(function (v) {
if (!visited[v]) {
visited[v] = true;
queue.push({ vertex: v, count: count + 1 });
}
});
}
}
function shortestPath(graph, source, target) {
if (source == target) { // Delete these four lines if
print(source); // you want to look for a cycle
return; // when the source is equal to
} // the target.
var queue = [ source ],
visited = { source: true },
predecessor = {},
tail = 0;
while (tail < queue.length) {
var u = queue[tail++], // Pop a vertex off the queue.
neighbors = graph.neighbors[u];
for (var i = 0; i < neighbors.length; ++i) {
var v = neighbors[i];
if (visited[v]) {
continue;
}
visited[v] = true;
if (v === target) { // Check if the path is complete.
var path = [ v ]; // If so, backtrack through the path.
while (u !== source) {
path.push(u);
u = predecessor[u];
}
path.push(u);
path.reverse();
print(path.join(' → '));
return;
}
predecessor[v] = u;
queue.push(v);
}
}
print('there is no path from ' + source + ' to ' + target);
}
function print(s) { // A quick and dirty way to display output.
s = s || '';
document.getElementById('display').innerHTML += s + '<br>';
}
window.onload = function () {
var graph = new Graph();
graph.addEdge('A', 'B');
graph.addEdge('B', 'C');
graph.addEdge('B', 'E');
graph.addEdge('C', 'D');
graph.addEdge('C', 'E');
graph.addEdge('C', 'G');
graph.addEdge('D', 'E');
graph.addEdge('E', 'F');
bfs(graph, 'A');
print();
shortestPath(graph, 'B', 'G');
print();
shortestPath(graph, 'G', 'A');
};
body {
font-family: 'Source Code Pro', monospace;
font-size: 12px;
}
<link rel="stylesheet" type="text/css"
href="https://fonts.googleapis.com/css?family=Source+Code+Pro">
<div id="display"></id>
我已经搜索了数周的时间来寻找计算 JavaScript 中最短路径的方法。我一直在玩 Data Structures and Algorithms 的书 https://github.com/loiane/javascript-datastructures-algorithms/tree/master/chapter09。
我不断发现的问题是代码太过定制以至于几乎不可能重写以产生所需的结果。我希望能够获得从任何给定顶点到任何其他顶点的最短路径,而不是像 Groner 编码的那样,只是 A 的所有内容的列表。例如,我希望能够获得来自F 到 B,或从 C 到 A。
完整代码在这里:http://jsfiddle.net/8cn7e2x8/
有人能帮忙吗?
var graph = new Graph();
var myVertices = ['A','B','C','D','E','F'];
for (var i=0; i<myVertices.length; i++) {
graph.addVertex(myVertices[i]);
}
graph.addEdge('A', 'B');
graph.addEdge('B', 'C');
graph.addEdge('B', 'E');
graph.addEdge('C', 'D');
graph.addEdge('C', 'E');
graph.addEdge('C', 'G');
graph.addEdge('D', 'E');
graph.addEdge('E', 'F');
graph.dfs();
console.log('********* sortest path - BFS ***********');
var shortestPathA = graph.BFS(myVertices[0]);
//from A to all other vertices
var fromVertex = myVertices[0];
for (i = 1; i < myVertices.length; i++) {
var toVertex = myVertices[i],
path = new Stack();
for (var v = toVertex; v !== fromVertex; v = shortestPathA.predecessors[v]) {
path.push(v);
}
path.push(fromVertex);
var s = path.pop();
while (!path.isEmpty()) {
s += ' - ' + path.pop();
}
console.log(s);
}
阅读您的问题,我可以通过以下两种方式之一阅读它... 要么你试图减少它检查的东西的数量,要么你试图让自己传入变量来改变端点。我将假设前者并让其他人处理后一种情况。
粗略浏览一下这个问题,您似乎遇到了 Comp Sci 中所谓的 "The traveling salesman problem." 这是计算机编程中的经典问题,被认为是逻辑上不可能的,并且是一个很好的问题"perfect being the enemy of the good."
的示例经典的旅行推销员问题是这样的,"Program a way for a salesman to reach all of his destination cities on a map in the shortest time. Do so without having to check every single possible path."事实是,(尚未)发现符合逻辑的方法(如果不可能或可能,则尚待证明)。也就是说,如果它不必是最短的,而只是一条较短的路径,则可以采用许多捷径。一个例子是从头到尾计算一条线,然后推入偏差以匹配最近的顶点。另一种方法是将路径分解成三角形,这些三角形仅将每个顶点连接到下一个最接近的两个顶点,然后以相同的方式连接团块,直到所有顶点都连接起来,然后只计算这些子集的起始潜在路径。
这两个答案都不能保证给你最好的答案,但它们会提供一个很好的答案,所需的计算时间要少得多,所以你不必计算来自 A、B 和 C 的每条路径等等等等
让我们首先说明,如果图未加权,广度优先搜索 (BFS) 会计算从给定源顶点开始的最短路径。换句话说,我们将路径的长度视为路径中的边数。
下面是构建未加权图的简单方法:
function Graph() {
var neighbors = this.neighbors = {}; // Key = vertex, value = array of neighbors.
this.addEdge = function (u, v) {
if (neighbors[u] === undefined) { // Add the edge u -> v.
neighbors[u] = [];
}
neighbors[u].push(v);
if (neighbors[v] === undefined) { // Also add the edge v -> u so as
neighbors[v] = []; // to implement an undirected graph.
} // For a directed graph, delete
neighbors[v].push(u); // these four lines.
};
return this;
}
请注意,我们已经实现了一个无向图。如行内注释中所述,您可以通过从 addEdge
函数中删除四行来修改代码以构建有向图。
BFS 的这种实现在有向图上同样有效:
function bfs(graph, source) {
var queue = [ { vertex: source, count: 0 } ],
visited = { source: true },
tail = 0;
while (tail < queue.length) {
var u = queue[tail].vertex,
count = queue[tail++].count; // Pop a vertex off the queue.
print('distance from ' + source + ' to ' + u + ': ' + count);
graph.neighbors[u].forEach(function (v) {
if (!visited[v]) {
visited[v] = true;
queue.push({ vertex: v, count: count + 1 });
}
});
}
}
要找到两个给定顶点之间的最短路径并显示沿路径的顶点,我们在探索图形时必须记住每个顶点的前导:
function shortestPath(graph, source, target) {
if (source == target) { // Delete these four lines if
print(source); // you want to look for a cycle
return; // when the source is equal to
} // the target.
var queue = [ source ],
visited = { source: true },
predecessor = {},
tail = 0;
while (tail < queue.length) {
var u = queue[tail++], // Pop a vertex off the queue.
neighbors = graph.neighbors[u];
for (var i = 0; i < neighbors.length; ++i) {
var v = neighbors[i];
if (visited[v]) {
continue;
}
visited[v] = true;
if (v === target) { // Check if the path is complete.
var path = [ v ]; // If so, backtrack through the path.
while (u !== source) {
path.push(u);
u = predecessor[u];
}
path.push(u);
path.reverse();
print(path.join(' → '));
return;
}
predecessor[v] = u;
queue.push(v);
}
}
print('there is no path from ' + source + ' to ' + target);
}
以下代码片段演示了您在问题中提供的图形上的这些操作。首先,我们找到从 A
可达的所有顶点的最短路径。然后我们找到从B
到G
和从G
到A
的最短路径。
function Graph() {
var neighbors = this.neighbors = {}; // Key = vertex, value = array of neighbors.
this.addEdge = function (u, v) {
if (neighbors[u] === undefined) { // Add the edge u -> v.
neighbors[u] = [];
}
neighbors[u].push(v);
if (neighbors[v] === undefined) { // Also add the edge v -> u in order
neighbors[v] = []; // to implement an undirected graph.
} // For a directed graph, delete
neighbors[v].push(u); // these four lines.
};
return this;
}
function bfs(graph, source) {
var queue = [ { vertex: source, count: 0 } ],
visited = { source: true },
tail = 0;
while (tail < queue.length) {
var u = queue[tail].vertex,
count = queue[tail++].count; // Pop a vertex off the queue.
print('distance from ' + source + ' to ' + u + ': ' + count);
graph.neighbors[u].forEach(function (v) {
if (!visited[v]) {
visited[v] = true;
queue.push({ vertex: v, count: count + 1 });
}
});
}
}
function shortestPath(graph, source, target) {
if (source == target) { // Delete these four lines if
print(source); // you want to look for a cycle
return; // when the source is equal to
} // the target.
var queue = [ source ],
visited = { source: true },
predecessor = {},
tail = 0;
while (tail < queue.length) {
var u = queue[tail++], // Pop a vertex off the queue.
neighbors = graph.neighbors[u];
for (var i = 0; i < neighbors.length; ++i) {
var v = neighbors[i];
if (visited[v]) {
continue;
}
visited[v] = true;
if (v === target) { // Check if the path is complete.
var path = [ v ]; // If so, backtrack through the path.
while (u !== source) {
path.push(u);
u = predecessor[u];
}
path.push(u);
path.reverse();
print(path.join(' → '));
return;
}
predecessor[v] = u;
queue.push(v);
}
}
print('there is no path from ' + source + ' to ' + target);
}
function print(s) { // A quick and dirty way to display output.
s = s || '';
document.getElementById('display').innerHTML += s + '<br>';
}
window.onload = function () {
var graph = new Graph();
graph.addEdge('A', 'B');
graph.addEdge('B', 'C');
graph.addEdge('B', 'E');
graph.addEdge('C', 'D');
graph.addEdge('C', 'E');
graph.addEdge('C', 'G');
graph.addEdge('D', 'E');
graph.addEdge('E', 'F');
bfs(graph, 'A');
print();
shortestPath(graph, 'B', 'G');
print();
shortestPath(graph, 'G', 'A');
};
body {
font-family: 'Source Code Pro', monospace;
font-size: 12px;
}
<link rel="stylesheet" type="text/css"
href="https://fonts.googleapis.com/css?family=Source+Code+Pro">
<div id="display"></id>