尝试构建一种方法,通过DAG获取所有可想到的唯一路径。接受递归,因为它似乎最容易理解。结束了这个:
public class Brutus {
//the previous nodes visited
public ArrayList<Node> resultHistory = new ArrayList<Node>();
//Directed Graph class, contains a HashMap [adjacencies]
// that has keys for all nodes that contains all edges
public AdjacencyList paths;
//A list of all the pathways between nodes represented as a list of nodes
public ArrayList<ArrayList<Node>> allPaths = new ArrayList<ArrayList<Node>>();
public Brutus(AdjacencyList paths) {
this.paths = paths;
}
public ArrayList<ArrayList<Node>> findAll() {
int counter = 1;
for (Node key : paths.adjacencies.keySet()) {
System.out.println("[" + counter + "]: " + key.toString());
StringTokenizer st = new StringTokenizer(
paths.getAdjacentString(key), ",");
while (st.hasMoreTokens()) {
String child = st.nextToken();
if (paths.getNodeFromGraph(child) != null) {
resultHistory = new ArrayList<Node>();
resultHistory.add(key);
findPath(child, resultHistory);
}
}
counter++;
}
return allPaths;
}
public void findPath(String child, ArrayList<Node> resultHistory) {
if (resultHistory.contains(paths.getNodeFromGraph(child))) {
return;
}
resultHistory.add(paths.getNodeFromGraph(child));
if(!(inList(resultHistory, allPaths))) {
allPaths.add(resultHistory);
}
StringTokenizer st = new StringTokenizer(
paths.getAdjacentString(paths.getNodeFromGraph(child)), ",");
while (st.hasMoreTokens()) {
child = st.nextToken();
if (paths.getNodeFromGraph(child) != null) {
findPath(child, resultHistory);
}
}
}
public boolean inList(ArrayList<Node> resultHistory,
ArrayList<ArrayList<Node>> allPaths) {
for (int i = 0; i < allPaths.size();i++) {
if (allPaths.get(i).equals(resultHistory)) {
return true;
}
}
return false;
}
问题是,我不认为它适用于所有路径,因为我无法在其中找到某些路径。虽然数据集是900个节点,但我无法找到模式!关于Stack的其他问题似乎更专业,因此我尝试构建自己的算法!
任何人都可以建议一种更好的方式来执行此操作,或者告诉我我做错了什么? 如果算法正确,那么撤销两个节点之间所有路径的最佳方法是什么?
编辑:我现在意识到新路径不是从原始的子节点创建的,我怎么能这样做呢?答案 0 :(得分:3)
这是一个简单的递归算法,用伪代码表示,以避免大量Java列表操作的问题:
AllPaths(currentNode):
result = EmptyList()
foreach child in children(node):
subpaths = AllPaths(child)
foreach subpath in subpaths:
Append(result, currentNode + subpath)
return result
在根节点上调用AllPaths
将为您提供所需的内容,并且您可以通过在每个节点上缓存AllPaths
的结果来改善非平凡DAG的运行时间,因此您只需要计算每一条包含它的不同路径,而不是一次。
答案 1 :(得分:3)
这里有一个基于BFS算法的实现。
我将路径表示为顶点序列l = (v, v', v'', ...)
,我将对其执行以下两个操作:
extend(l, v)
:将顶点v
放在列表l
的末尾; v = back(l)
:获取列表l
中的最后一个顶点。FindPaths(G, v) {
// The first path is, simply, the starting node.
// It should be the first vertex in topological order.
pending_paths = {(v)};
while (pending_paths is not empty) {
l = pending_paths.remove_first(); // pop the first pending path
output(l); // output it (or save it in a list to be returned, if you prefer)
v = back(l); // Get the last vertex of the path
foreach(edge (v, v') in G) { // For each edge outgoing from v'...
// extend l with v' and put into the list of paths to be examined.
pending_paths.push_back(extend(l, v'));
}
}
}
答案 2 :(得分:2)
所以虽然@ akappa的Pseudo是一个好的开始,但我花了一些时间来理解如何让它工作,如果有人在这里发现这个帖子我就是这样做的:
public ArrayList<ArrayList<Node>> searchAll() {
try {
BufferedWriter out = new BufferedWriter(new FileWriter("output.txt"));
//Gets Nodes from Hashmap and puts them into Queue
for (Node key : paths.adjacencies.keySet()) {
queue.addToQueue(new QueueItem(key.chemName, new ArrayList<Node>()));
}
while (queue.getSize() > 0) {
QueueItem queueItem = queue.getFromQueue();
Node node = paths.getNodeFromGraph(queueItem.getNodeId());
if (node != null) {
findRelationAll(node, queueItem, out);
}
}
System.out.println("Cycle complete: Number of Edges: [" + resultHistoryAll.size() + "]");
out.close();
} catch (IOException e) {
}
return resultHistoryAll;
}
public void findRelationAll(Node node, QueueItem queueItem, BufferedWriter out) {
if (!foundRelation) {
StringTokenizer st = new StringTokenizer(paths.getAdjacentString(node), ",");
while (st.hasMoreTokens()) {
String child = st.nextToken();
ArrayList<Node> history = new ArrayList<Node>();
//Gets previous Nodes
history.addAll(queueItem.getHistoryPath());
//Makes sure path is unique
if (history.contains(node)) {
System.out.println("[" + counter2 + "]: Cyclic");
counter2++;
continue;
}
history.add(node);
resultHistory = history;
queue.addToQueue(new QueueItem(child, history));
if (!(inList(resultHistory, resultHistoryAll))) {
resultHistoryAll.add(resultHistory);
try {
out.write("[" + counter + "]: " + resultHistory.toString());
out.newLine();
out.newLine();
} catch (IOException e) {
}
System.out.println("[" + counter + "]: " + resultHistory.toString());
counter++;
} else {
System.out.println("[" + counter3 + "]: InList");
counter3++;
}
}
}
}
//This checks path isn't in List already
public boolean inList(ArrayList<Node> resultHistory, ArrayList<ArrayList<Node>> allPaths) {
for (int i = 0; i < allPaths.size(); i++) {
if (allPaths.get(i).equals(resultHistory)) {
return true;
}
}
return false;
}
}
上面的代码会做一些你可能不想要的事情:
QueueItem对象只是存储以前访问过的节点的一种方法。它是nemanja's代码的一部分,这是我的代码所基于的。
向他提示,akappa(最有效的答案)和jacobm(用于找到像我原始代码的解决方案,并解释它的局限性)。
任何人真正对这项工作感兴趣;我目前正处理超过500万个途径,其中60,000个是900种化学品之间的独特途径。这只是1,2,3或4种化学途径......生物学很复杂。
编辑和警告:如果有人正在处理像我这样的大量数据,Windows 7 - 或者至少是我的机器 - 抛出一个合适的东西并在ArrayList&gt;之后关闭程序。 63,000个对象,无论你如何安排指针。我开始使用的解决方案是在60,000个对象之后,重新启动列表,同时将所有内容添加到CSV。这导致了列表迭代之间的一些重复,并且最终应该被我明天转移到linux上超越!