我有一个由Node
个对象连接的Edge
个对象的图表,需要按以下方式进行探索:
我获得了一个起始Edge source
,需要查找所有其他Edge
个对象,以便沿路径传递的边长度总和不超过MAX_RANGE
并执行符合条件的每个Edge
的一些操作。
我对此问题的解决方案是递归分支,随时跟踪行进距离(Edge#getEdgeConnections()
返回包含连接到{{1}的所有ArrayList<Edge>
个对象的Edge
它被召唤了):
Edge
现在,这应该可以正常工作,除了一件事 - 图表包含几个循环的情况。
因此,如果递归算法以循环周围较长的路径开始,则可能会错过选择较短路径时指定范围内的某些边缘,如下所示
private final ArrayList<Edge> occupiedEdges = new ArrayList<>();
private void doStuffWithinRangeOf(Edge source) {
doStuffAtEdge(source);
for (Edge connection : source.getEdgeConnections()) {
doStuffAtBranch(connection, source, 0);
}
}
private void doStuffAtBranch(Edge edge, Edge source, double distance) {
double newDistance = distance + edge.getLength();
doStuffAtEdge(edge);
for (Edge connection : edge.getEdgeConnections()) {
if (!connection.equals(source)
&& !isOccupied(connection)
&& (newDistance < MAX_AP_RANGE)) {
doStuffAtBranch(connection, edge, newDistance);
}
}
}
private void duStuffAtEdge(Edge edge) {
occupiedEdges.add(edge);
... // Some amount of work that mustn't be done more than once per Edge
}
private boolean isOccupied(Edge edge) {
return occupiedEdges.contains(edge);
}
现在,我想到的解决方案是做一个不同的搜索模式,在那里我将有一个&#34; frontier&#34;的列表(或地图)。边缘,并按照距离增加的顺序探索它们(每次探索边缘时都会向边界添加新边缘)。
可能涉及大量边缘,因此我不是每次都要遍历整个列表,以找到距离源最短距离的那个。
是否有某种类型的集合能够以这种方式自动保存订单并且在添加/删除元素方面有效?
SortedMap是我要找的吗?在这种情况下我如何使用它?
修改
感谢所有响应者。我最终使用带有包装类的 ----------- C ---- D - E // The algorithm explores AB, BC, CD and makes them occupied
| |
B | // DE is too far along this path, and isn't occupied
| |
------------A // When the algorithm explores along AC, it finds that CD
// is already occupied and stops
// even though DE is really within range
(有关详细信息和代码,请参阅my answer)。
答案 0 :(得分:2)
我建议您调整算法,而不是使用其他一些数据结构:
您已经实施了某种depth-first-search来浏览图表。如果您使用某种breadth-first-search,则可以在达到指定范围时停止,并且只访问范围内的每个边缘一次(通过使用已经实现的isOccupied逻辑)。
答案 1 :(得分:1)
您正在寻找的算法是经过修改的Dijkstra,而不是搜索从A到B的最短路径,而是搜索所有短于X的最短路径.Dijkstra保证您将访问每个节点从开始的距离递增,并从开始通过最短路径。此外,如果没有负长度边缘,那么父母身份将永远不会改变 - 并且保证内部if
将沿着到节点的最小路径的每条边执行一次且仅执行一次。但是,由于“比X更接近的节点”的集合仅在结尾处是已知的(=具有最终距离&lt; max的那些节点),因此您可以等到算法结束到doStuffAtBranch
仅用于实际引导某个地方有趣的分支
伪代码如下:
final HashMap<Node, Double> distances = new HashMap<>();
HashMap<Node, Node> parents = new HashMap<>();
distances.put(start, 0); // start is at distance 0 from start
PriorityQueue<Vertex> q = new PriorityQueue(allVertices.size(),
new Comparator<Vertex>() {
public int compare(Vertex a, Vertex b) {
return distances.get(a) - distances.get(b);
}
});
for (Vertex v : allVertices) {
q.add(v, distances.contains(v) ?
distances.get(v) : Double.POSITIVE_INFINITY);
}
while ( ! q.isEmpty()) {
Vertex u = q.poll(); // extract closest
double current = distances.get(u);
if (current > max) {
// all nodes that are reachable in < max have been found
break;
}
for (Edge e : u.getEdges()) {
Vertex v = u.getNeighbor(e);
double alt = current + e.length();
if (alt < distances.get(v)) {
q.remove(v); // remove before updating distance
distances.put(v, alt);
parents.put(v, u); // v will now be reached via u
q.add(v); // re-add with updated distance
// if there are no negative-weight edges, e will never be re-visited
}
}
}
答案 2 :(得分:1)
此Java实现使用DFS,该图表示为Adjacency Matrix。还对已处理节点使用int数组,并使用int数组标记节点与startNode的距离
public List<Integer> findMinDistantNodesUsingBF(int[][] graph, int startNode, int distance) {
int len = graph.length;
boolean[] processed = new boolean[len];
int[] distanceArr = new int[len];
LinkedList<Integer> queue = new LinkedList<Integer>();
List<Integer> result = new ArrayList<Integer>();
queue.add(startNode);
processed[startNode] = true;
distanceArr[startNode] = 0;
while (!queue.isEmpty()) {
int node = queue.remove();
for (int i = 0; i < len; i++) {
if (graph[node][i] == 1 && !processed[i]) {
if (distanceArr[node] == distance - 1) {
result.add(i);
} else {
queue.add(i);
distanceArr[i] = distanceArr[node] + 1;
}
processed[i] = true;
}
}
}
return result;
}
答案 3 :(得分:0)
您可以使用BFS或DFS的修改,您还可以为路径的MAX_LENGTH添加额外规则(除了检查您是否访问过所有邻居节点)。
我建议您选择DFS,因为它与您目前所做的有点接近。您也可以轻松地“插入”doSomethingToEdge中的任何一种方法
答案 4 :(得分:0)
注意:在写完答案之后,我意识到你正在谈论边缘,但是我在节点方面提到的同样的例子和相同的理论也适用于边缘。
如果您只是简单地使用BFS,那么它将无法正常工作,因为您会过早地将node
标记为已访问过。考虑这个例子
你的max_range是20&amp; d(x,y)
表示x
和y
A->(10)E->D(10)->(5)F // d(A,E)=d(E,D)=10 & d(D,F)=5
A->(5)B->(5)C->(5)D->(5)F //d(A,B)=d(B,C)=d(C,D)=d(D,F)=5
在这种情况下,您首先会在路径(A-> E-> D)
中找到D(因为它在此路径中更接近A),并且您将D
标记为已访问。这实际上是错误的,因为将D标记为已访问会阻止您访问F
,如果您的路径是(A->B->C->D->F)
,则可以访问List of nodes
。
为了避免重复并解决此问题,对于您要添加的每个节点,您还应添加在当前路径中看到的F
。这样您仍然可以访问D
,因为当您在路径(A->B->C->D)
中到达 NodeWrapper{
Node node;
List<Node> Parents;
int pathSum;
}
时,您会看到它未被访问,因为它没有出现在您的路径中。
来实施,我会给你一个粗略的想法:
创建一个包装类
{
Queue<NodeWrapper> queue = new LinkedList<>();
Queue.add(new NodeWrapper(sourceNode,new ArrayList<Node>));
while(!queue.isEmpty()){
NodeWrapper temp = queue.poll();
Node presentNode = temp.getNode();
List<Node> parentsList = temp.getParentsList();
for all neighbours of presentNode
if neighbour is not in presentNode && (pathSum +currentEdgeWeight )< max && your conditions
add currentNode to parent list
queue.add(new NodeWrapper(neighbour,parentList,(pathSum +currentEdgeWeight)));
}
}
您的BFS应如下所示:
<a href="http://drive.google.com/drive/folders/folderidgoeshere" target="_blank">Drive button</a>
答案 5 :(得分:0)
经过几次迭代后,我最终使用了类似to that of user tucuxi的解决方案。我使用PriorityQueue
和一个实现Comparable
接口的包装类。当我意识到我需要在几个不同的情况下探索图形并做不同的事情时,我在Edge
类中创建了一个通用的方法,它返回所提供范围内的所有其他边。
<强>代码:强>
public ArrayList<Edge> uniqueEdgesWithinRange(double range) {
ArrayList<Edge> edgeList = new ArrayList<>();
PriorityQueue<ComparableEdge> frontier = new PriorityQueue<>();
frontier.add(new ComparableEdge(0.0, this));
while(!frontier.isEmpty()) {
ComparableEdge cEdge = frontier.poll();
edgeList.add(cEdge.edge);
if (cEdge.distance < range) {
for (Edge connection : cEdge.edge.getEdgeConnections()) {
if (!edgeList.contains(connection)) {
frontier.add(new ComparableEdge(cEdge.distance + connection.getLength(), connection));
}
}
}
}
return edgeList;
}
private class ComparableEdge implements Comparable<ComparableEdge> {
private double distance; // Distance from closest point on source to furthest point on edge
private Edge edge;
private ComparableEdge(double distance, Edge edge) {
this.distance = distance;
this.edge = edge;
}
@Override
public int compareTo(ComparableEdge another) {
return Double.compare(distance, another.distance);
}
}
该方法中的缩进量使我感觉很高兴,所以我可能会重构它,但是否则它应该在功能上完整。
答案 6 :(得分:0)
// Finds all nodes that are maximum x distance away from given node.
public Set<Integer> findMaxDistantNodesRecurse(int[][] graph, int startNode, int distance) {
int len = graph.length;
Set<Integer> set = new HashSet<Integer>();
for (int j = 0; j < len; j++) {
if (startNode == j)
continue;
if (graph[startNode][j] == 1) {
set.add(j);
if (distance > 1)
set.addAll(findMaxDistantNodesRecurse(graph, j, distance - 1));
}
}
return set;
}