递归:如何获取某个节点的所有(子)边?

时间:2012-03-22 13:26:43

标签: javascript recursion graph

我的问题如下。我有一个来自图形的源/目标形式的对象数组(这些是节点的id)。它看起来像这样:

[{"source": 1053, "target": 845, "value": 751}, {"source": 845, "target": 862, "value": 751}, {"source": 1053, "target": 611, "value": 751}, {"source": 1053, "target": 611, "value": 751}, {"source": 1054, "target": 905, "value": 17}, {"source": 1055, "target": 837, "value": 8}, {"source": 1055, "target": 837, "value": 8}, {"source": 1055, "target": 837, "value": 8}, {"source": 1055, "target": 400, "value": 8}, {"source": 1055, "target": 400, "value": 8}, {"source": 1055, "target": 400, "value": 8}]

现在:对于某个节点id,我想知道它的所有子节点以及这些子节点的后续子节点,等等,一直向下。它适用于第一代“生成”,但随后我需要一次又一次地遍历目标。我该如何做到这一点?

nodes = [1053]
function getChildren(nodes, links) {
    var children = [];
    $.each(links, function(key, link) {
        if (nodes.indexOf(link.source) > -1) {
            children.push(link.target);
        }
    });
    return children;
}

3 个答案:

答案 0 :(得分:1)

递归看起来像:

  1. 获取给定节点的所有孩子
  2. 如果其中一个对您的结果数组是新的
    • 获取所有孩子并添加
  3. 返回结果
  4. ...这将是一个简单的depth-first search

    function getAllLinkedTo(nodes) {
        var result = [];
        for (var i=0; i<nodes.length; i++)
            getChildren(nodes[i]);
        function getChildren(node) {
            if (result.indexOf(node) > -1)
                return;
            result.push(node);
            for (var i=0; i<links.length; i++) // a source -> targets mapping object would be beneficial
                if (links[i].source == node)
                    getChildren(links[i].target);
        }
        return result;
    }
    

    你也可以通过result循环完成此任务,这可能会更快。

答案 1 :(得分:1)

  1. 获得节点的子节点列表后,只需遍历数组并再次调用getChildren。无论你是否以递归方式执行此操作取决于您是想要先深度优先还是广度优先(请参阅Bergi的答案)。

  2. 请注意,如果图中有循环,则一个简单的实现将永远递归,至少在它达到最大递归深度或堆栈溢出之前。要安全地执行此操作,您需要传递已在层次结构中看到的给定节点的节点列表,您在递归之前检查该节点。

  3. 考虑使用sourcenode创建一个更简单的地图结构:targetnode作为键:值对,这样你就可以进行查找而不是indexOf,这会慢一些。

  4. 如果您需要所有节点的所有子节点的列表,请考虑构建所有节点的树,否则如果它是多个边的目标,您将在给定节点上多次递归。

答案 2 :(得分:1)

另一种算法,有点像Phil H的回答。它是广度优先的,并使用辅助地图结构。

Array.prototype.combine = function(a) {
    for (var i=0; i<a.length; i++)
        if (this.indexOf(a[i]) == -1)
            this.push(a[i]);
    return this;
};

var map; // cache
function buildMap(links) {
    return links.reduce(function(map, val) {
        if (!map[val.source])
            map[val.source] = [];
        map[val.source].push(val.target);
    }, {});
}

function getLinked(nodes) {
    if (!map)
        map = buildMap(links);
    var result = nodes.slice(0);
    for (var i=0; i<result.length; i++)
        result.combine(map[result[i]]);
    return result;
}