查找邻接列表中的所有连接节点

时间:2014-02-18 20:42:08

标签: algorithm graph directed-acyclic-graphs

我有一个DAG的邻接列表,我需要从所有节点中找到所有连接的节点,例如:对于下面的DAG

1 -> 3 -> 4  
2 -> 4  
3 -> 2    
4 -> 5  
5 -> NULL  

我需要这个:

1 -> {2, 3, 4, 5}
2 -> {4, 5}
3 -> {2, 4, 5}
4 -> {5}
5 -> NULL  

这有什么有效的算法吗?

4 个答案:

答案 0 :(得分:3)

正如您提到的DAG,您可以使用以下算法将所有连接的组件提供给给定的顶点: -

  
      
  1. 对图中的所有节点进行拓扑排序
  2.   
  3. 以排序节点的降序开始。
  4.   
  5. 为每个节点维护一组所有连接的节点。
  6.   
  7. 按照订单顺序排列顶点u的邻接列表
  8.   
  9. 对于顶点u和每个边(u,v)和!Set(u).contains(v)do Set(u)= Set(u)union Set(v)union v
  10.   
  11. 按照toposort的降序对所有节点执行此操作。
  12.   

时间复杂度: -

Toposort: O(E)

计算集: O(V^2*logV)

总计: O(V^2*logV)

示例: -

1 -> 3 -> 4  
2 -> 4  
3 -> 2    
4 -> 5  
5 -> NULL  

TopoSort: 1,3,2,4,5

Visiting in descending order :-
1. Set(5) = {null}
2. Set(4) = Set(5) + 5 = {5}
3. Set(2) = Set(4) + 4 = {4,5}
4. Set(3) = Set(2) + 2 = {2,4,5}
5. Set(1) = Set(3) + 1 = {1,2,4,5} 

答案 1 :(得分:2)

最直接的方法是递归地下降图表。伪代码:

function get_connected_nodes(node){
    nodes = set();
    foreach(child in node.children){
        nodes.add(child);
        nodes = nodes.union(get_connected_nodes(child));
    }
    return nodes;
}

示例Python实现:

adjacencies = {
    1: [3],
    2: [4],
    3: [2, 4],
    4: [5],
    5: []
}

def get_connected_nodes(node):
    nodes = set()
    for child in adjacencies[node]:
        nodes.add(child)
        nodes = nodes | get_connected_nodes(child)
    return nodes

for i in range(1, 6):
    print i, ":", get_connected_nodes(i)

#output:
#1 : set([2, 3, 4, 5])
#2 : set([4, 5])
#3 : set([2, 4, 5])
#4 : set([5])
#5 : set([])

编辑:为了提高性能,存储以前计算的结果可以避免多次遍历节点。伪代码:

connected_results = dict();
function get_connected_nodes(node){
    if(!connected_results.has_key(node)){
        nodes = set();
        foreach(child in node.children){
            nodes.add(child);
            nodes = nodes.union(get_connected_nodes(child));
        }
        connected_results[node] = nodes;
    }
    return connected_results[node];
}

示例Python实现:

def memoize(fn):
    answers = {}
    def memoized_fn(*args):
        if args not in answers:
            answers[args] = fn(*args)
        return answers[args]
    return memoized_fn

adjacencies = {
    1: [3],
    2: [4],
    3: [2, 4],
    4: [5],
    5: []
}

@memoize
def get_connected_nodes(node):
    nodes = set()
    for child in adjacencies[node]:
        nodes.add(child)
        nodes = nodes | get_connected_nodes(child)
    return nodes

for i in range(1, 6):
    print i, ":", get_connected_nodes(i)

答案 2 :(得分:1)

您正在寻找的东西似乎是DAG传递闭包的邻接列表。

因为你需要效率:你有一个DAG,所以它没有周期,这意味着你可以通过DFS决定来自O(m + n)中给定起始顶点的所有顶点的可达性/ BFS。对n个顶点中的每一个执行此操作一次,最后得到O(n *(m + n)),对于具有Omega(n)边的图,它为O(n * m)。

如果你有巨大的密集DAG,矩阵乘法方法(取邻接矩阵,对它进行平方,只要adjacencyMatrix [i] [j]> 0),你就得到了一条边(i,j)快速矩阵多重化可能更快;确保相关的基准测试,因为尽管矩阵乘法方法具有渐近的优越性,但在大多数应用中,它确实往往不会优于上述方法。

如果你真的处理密集的图形(即Omega(n ^ 2)边缘)你可能也想考虑使用Ford-Warshall,甚至认为它不会超越BFS方法,纯粹是因为你' ll可能会找到一个位于某处的实现(如果没有,谷歌帮助)。

答案 3 :(得分:0)

您将获得有向非循环图(DAG),并且您打算查找从给定节点可到达的所有节点。请考虑以下重复解决方案,其中f(u)表示可从u访问的节点集。

这只是意味着您可以从u到达的节点是您可以从其所有邻居(相邻节点)到达的节点的并集。这可以通过DFS(深度优先搜索)以及先前计算的f(u)集的记忆容易地实现。此方法的时间复杂度为O(|E| + |V| + |E||V|log(|V|)),空间复杂度为O(|V|^2)。请注意,由于|V|^2内存要求,此算法无法很好地扩展到非常大的图形。

以下C ++代码演示了这种方法:

#include <iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<vector>
#include<set>

using namespace std;

#define FOR(i,n) for(int i=0;i<n;++i)
#define FORS(i,s,n) for(int i=s;i<n;++i)
#define MAX 1000000

#define WHITE 0
#define GRAY 1
#define BLACK 2

void dfs_visit(vector<int> adj[], int u, int color[], set<int> cache[])
{
 color[u]=GRAY;
 int len = adj[u].size();

 FOR(i,len)
 {
  int v = adj[u][i];
  if(color[v]==WHITE)
  {
   dfs_visit(adj,v,color,cache);
  }          
 }  

 // Before coloring a node black, set its f(u) to be the union of the f(v) of its neighbours for all v
 FOR(i,len)
 {
    int v = adj[u][i];
    cache[u].insert(v);
    // This will take time logarithmic in the size of f(u) since each insertion requires comparison for uniqueness
    for(set<int>::iterator it = cache[v].begin(); it!=cache[v].end(); ++it)
    {
        cache[u].insert(*it);
    }
 }

    color[u]=BLACK;   
}

void dfs(vector<int> adj[], set<int> cache[], int n)
{
 int color[n];
 memset(color, WHITE, sizeof(color));

 FOR(i,n)
 {
    if(color[i]==WHITE)
    {
     dfs_visit(adj, i, color, cache);
    }                      
 }      
}

int main()
{
    int n, m;
    scanf("%d%d", &n, &m);

    // To handle 0-based index, initialize as n+1
    set<int> cache[n+1];
    vector<int> adj[n+1];

    FOR(i,m)
    {
        int u,v;
        scanf("%d%d", &u, &v);
        adj[u].push_back(v);
    }

    dfs(adj, cache, n);

    FORS(i,1,n)
    {
        set<int> fu = cache[i];
        printf("%d->{", i); 

        for(set<int>::iterator it=fu.begin(); it!=fu.end(); ++it)
        {
            printf("%d,", *it);     
        }

        printf("}\n");
    }

    return 0;
}

输入(N =节点数,M =边数,M行跟边缘列表):

5 6
1 3
2 4
3 4 
3 2
4 5

输出:

1->{2,3,4,5,}
2->{4,5,}
3->{2,4,5,}
4->{5,}