【问题标题】:Finding Shortest path in DAG (unweighed), between 2 vertices在 DAG(未加权)中查找 2 个顶点之间的最短路径
【发布时间】:2010-06-30 10:50:46
【问题描述】:

在 Floyd–Warshall/Dijkstra 回复洪水到来之前,请让我解释一下情况,因为我确信任何一种算法都可以针对这种情况进行调整,并且必须如此,因为这不是一个玩具示例程序(请注意, 在 java 中所以必须保持它在内存方面的可管理性)

我所拥有的是从节点 0 到节点 n 生成的网络图,节点 3 无法链接到节点 5,因为当节点 3 选择它的 外链接 时节点 5 不存在。每个“节点”都表示为 in_neighbours[nodeID] 和 out_neighbours[nodeID] 表示 nodeId=3,所以我们谈论的是节点 3。还要注意 in_/out_ 都已排序,(in_ 自然排序为 5 将选择它的 out 链接一次全部,只有这样 6 才会选择 out_links,因此 3 的 in_ 永远不能包含 {6, 5, 7}) 并且 ofc 都可以包含重复项。 (in/out 是大小为 n 的 ArrayList 数组,其中 out_ 的大小始终为 d 或 m,与 n 一起由用户在启动时指定)

没有重量。我必须做的是找到averageDistance()

public double getAvgDistance() {
    int sum = 0;        

    for (int i=1; i<n; i++) {
        for (int j=0; j < i; j++) {
            sum += dist(i, j);             // there are duplicates, make sure i skip 
        }
    }

    return (double)sum / (double)(  ((n*(n-1)) / 2)  );
}

我目前的情况是最好的。注意我只想找到j和i之间的距离,而不是同时找到所有的距离(内存不够,会在m=20 d=1 000 000时测试)

private int dist(int i, int j) {
    int dist = 0;

    for (int link : in_neighbours[j]) {
        System.out.print("\nIs "+j+" linked to by "+i);
        if (out_neighbours[i].contains(link)) {
            System.out.print(" - yes!");
            dist = 1;
        }
    }

    return dist;
}

所以我询问“更新鲜”(ofc 此时图表已完成)节点 i 是否直接链接到其任何较老的伙伴,如果是,则距离为 1 跳。

如果节点向后遍历,是我还是“最短”路径将始终是第一个找到的路径?

我如何检查它是否不是 1,即基本情况之后的“else”?我的数学很弱请温柔:) 任何提示如何利用链接已排序这一事实?

这不是家庭作业或我试图欺骗的东西,这与代码本身无关,这必须是一个有用的工具,“学习”一路走来。

这是一个图的外观 nodeID,out links,in links for m=7 n=13,(注意 0 个周期就是图的初始化方式):

0 | 0 0 0 0 0 0 0  | 0 0 0 0 0 0 0 1 1 1 1 1 1 1 2 2 2 2 2 3 4 5 6 6 7 8 9 
1 | 0 0 0 0 0 0 0  | 2 2 3 4 5 5 8 12 
2 | 0 0 0 0 0 1 1  | 3 3 3 3 3 4 4 4 6 7 8 10 
3 | 0 1 2 2 2 2 2  | 4 4 5 5 6 6 7 11 
4 | 0 1 2 2 2 3 3  | 5 5 6 8 9 10 
5 | 0 1 1 3 3 4 4  | 6 7 8 9 9 11 12 
6 | 0 0 2 3 3 4 5  | 7 7 7 8 9 9 12 
7 | 0 2 3 5 6 6 6  | 8 9 10 11 11 12 
8 | 0 1 2 4 5 6 7  | 10 10 10 11 12 
9 | 0 4 5 5 6 6 7  | 10 11 11 
10 | 2 4 7 8 8 8 9  | 12 12 
11 | 3 5 7 7 8 9 9  | 
12 | 1 5 6 7 8 10 10  | 

抱歉,阅读了令人痛苦的长篇文章。 编辑:方法中的代码错误,这是我现在认为正确的。

dist nr2 的修改,试试看有没有路径:

private int dist(int i, int j) {
    int dist = 0, c = 0, count = 0;
    boolean linkExists = false;

    for (int link : in_neighbours[j]) {

        //System.out.print("\nIs "+j+" linked to by "+i);
        if (out_neighbours[i].contains(link)) {

            //System.out.print(" - yes!");
            dist = 1; // there is a direct link

        } else {

            while ( c < j ) {
                // if there's a path from 0 up to j, check if 'i' links to a node which eventually links to 'j'
                if (out_neighbours[i].contains(c) && 
                        (out_neighbours[c].contains(link) || in_neighbours[c].contains(link) )) {

                    count++; // yes. and this is one node we had to step through to get closer
                    linkExists = true;
                } else {
                    linkExists = false; // unreachable, the path was interrupted somewhere on the way
                    break;
                }
                c++; 
            }

            if (linkExists) {
                dist = count-1; // as 2 nodes are linked with 1 edge
            } else {
                dist = 0; // no path was found
            }
        }


    }

    return dist;
}

【问题讨论】:

  • 两个问题:为什么你认为路径A->B->C的距离=1?而且,为什么在传入/传出结构中有重复项?这与最短路径搜索无关。
  • 好的,现在我看到你只计算路径中的跳数(即路径的内部节点),所以我的一个问题得到了回答......
  • 重复,因为添加链接的方式其中一种方法是幂律,所以如果一个节点有更多链接到它,包括重复,它仍然“更受欢迎”。是否应该在搜索开始之前消除重复项?

标签: java graph path directed-acyclic-graphs


【解决方案1】:

由于模型中所有边的权重相同,您可以使用BFS 搜索来查找从 S 到 T 的最短路径。

这是一个迭代过程,从集合 #0 开始,仅包含源节点 ({S})。 在每个步骤 i 中,您通过在一个步骤中找到集合 (i-1) 中可实现的所有节点来创建集合 #i。

迭代在两种情况下终止:

1) 当你检测到集合#k 包含 T。在这种情况下你返回 k-1。

2) 当集合为空时,表示两个节点不可达。

内存消耗大约是节点数的两倍,因为在每个步骤 i 中,您都在使用两个集合(i-1 和 i),受节点总数的限制。

--编辑--

这是一个可能的实现(我做了一些测试):

private Integer getDist(int i, int j) {
    Set<Integer> currentSet = new HashSet<Integer>();
    currentSet.add(i);
    int dist = 0;
    while (true) {
        Set<Integer> nextSet = new HashSet<Integer>();
        for (Integer currNode : currentSet)
            nextSet.addAll(out[currNode]);

        if (nextSet.isEmpty())
            return null; //i.e. infinite
        if (nextSet.contains(j))
            return dist;
        dist++;
        currentSet = nextSet; 
    }
}

实现假设inout定义为List[],节点用从0开始的数字标识。最小距离计为路径中的中间节点数,而不是边数。

您在列表中的重复项不会破坏任何内容,但它们与算法无关。

【讨论】:

  • 确实有用,我一直在考虑这样的事情,到目前为止,尽管它仍然不正确,但我不能在找到相关链接后立即停止感兴趣的(也许这就是你的意思)在我上面的问题中添加代码。
  • @Dima:我编辑了我的回复,并添加了我的代码以使其更清晰。是的,一旦找到目标节点,循环就会停止。
  • 我想我明白你在做什么了。但大多数时候它进入一个无限循环,如果它没有返回一个负值,我的 out[](out_neighbors[] 我假设)确实是一个 ArrayLists 数组,所以 out[currNode] 是一个 ArrayList节点currNode的传出链接。
  • 好吧忘记负值,但大多数情况下即使在微不足道的小图上也会永远循环。
  • @Dima:我不明白这是怎么发生的,除非你的结构包含非法的循环。您能否提供导致无限循环的最小输入?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-18
  • 2021-05-25
相关资源
最近更新 更多