JointJs循环检测

时间:2016-09-11 00:51:42

标签: javascript jointjs

我正在尝试使用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;
    }

如果有人能帮助我,那将是非常有帮助的。

2 个答案:

答案 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就像这样。我们的检测必须从A1A2开始。

我修改了符合以前策略的方法:

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;
}