我正在尝试使用JointJS实现一种算法来检测有向图中的循环(仅出站连接)。我编写了以下代码,但预期无法执行。
var _elements = graph.getElements();
var visited = [];
var level = 0;
var isCycleExists;
for (var i = 0; i < _elements.length; i++) {
var elem = _elements[i];
//only checking nodes which do have predecessors
if ((graph.getPredecessors(elem).length > 0) && !elem.isLink()
&& hasCycle(elem, visited, level)) {
isCycleExists = true;
break;
}
}
function hasCycle(comp, visited, level) {
var successors = graph.getSuccessors(comp);
visited.push(comp.id);
for (var i = 0; i < successors.length; i++) {
var c = successors[i];
var _successors = graph.getSuccessors(c);
if (_successors.length == 0) {
continue;
}
if (visited.indexOf(c.id) > -1) {
return true;
}
visited.push(c.id);
if (hasCycle(c, visited.slice(), ++level)) {
return true;
}
}
return false;
}
如果有人能帮助我,那将是非常有帮助的。
答案 0 :(得分:0)
问题是继任者与直接继任者不一样。检查graph.getSuccessors(comp)
值:如果lib使用一致的函数名,那么第一次运行时应该包含B,C,D和E.所以你标记了那些被访问过的人,但也运行hasCycle(c, visited.slice(), ++level)
,你再次从D开始检查节点(我猜D是第一个你得到#34;已经访问过的&#34; case)。
首先,我建议在你的函数中摆脱加倍迭代,像
一样 function hasCycle(comp, visited, level) {
var successors = graph.getSuccessors(comp), i;
if (visited.indexOf(comp.id) > -1)
return true;
visited.push(comp.id);
for (i = 0; i < successors.length; i++)
if (hasCycle(successors[i], visited.slice(), ++level))
return true;
return false;
}
第二,更重要的是,尝试使用graph.getNeighbors
方法而不是graph.getSuccessors
(请记住只检查一个方向的邻居)。
答案 1 :(得分:0)
我相信我已经找到了使用这种算法导致这种不稳定行为的根本原因。
实际上,JointJS API方法getElements()
按图表单元格中的条目顺序返回元素数组。这意味着,如果您创建3个元素A, B, C
来创建类似A-->B-->C
的图表,但您已按照A
首先添加这些元素,C
秒和B
在末尾。现在根据if条件(graph.getPredecessors(elem).length > 0) && !elem.isLink() && hasCycle(elem, visited, level)
,循环检测实际上从C
开始。在这种情况下,C
没有任何出站邻居,并且标记为已访问。现在,在下一次迭代中,检查B
(因为它在C
之后输入到图中)并且它有C
作为其出站邻居。现在再次评估C
,但C
已标记为已访问。因此,它表明存在一个循环。
所以,我相信,我们在图表中的搜索应该从没有任何前辈的节点开始。例如,在前面的示例中,我们的搜索必须始终以A
开头。该图还可能包含一个或多个不具有任何前任的节点。在这种情况下,我们还需要从没有任何前任的节点开始搜索。例如,A1-->B, A1-->C, A2-->B, A2-->C
就像这样。我们的检测必须从A1
和A2
开始。
我修改了符合以前策略的方法:
function checkForCycleExistence() {
var visited = [];
var level = 0;
var isCycleExists;
var _elements = graph.getElements();
for (var i = 0; i < _elements.length; i++) {
var elem = _elements[i];
if ((graph.getPredecessors(elem).length == 0)
&& hasCycle(elem, visited, level)) {
isCycleExists = true;
break;
}
}
if (isCycleExists) {
console.log("Cycle Exists");
}
return isCycleExists;
}
function hasCycle(comp, visited, level) {
var neighbors = graph.getNeighbors(comp, {
outbound : true
}), i;
if (visited.indexOf(comp.id) > -1)
return true;
visited.push(comp.id);
for (i = 0; i < neighbors.length; i++)
if (hasCycle(neighbors[i], visited.slice(), ++level))
return true;
return false;
}