簡體   English   中英

當這個常規隊列版本也正確時,為什么 Dijkstra 的算法需要優先級隊列?

[英]Why does Dijkstra's algorithm need a priority queue when this regular queue version is also correct?

我已閱讀以下內容,但請查看下面的代碼。

為什么 Dijkstra 的算法使用堆(優先隊列)?

我有兩個版本的 dijkstra,一個帶有 PQueue 的好版本,一個帶有常規鏈表隊列的壞版本。

public static void computeDijkstra(Vertex source) {
    source.minDistance = 0.;
    Queue<Vertex> vertexQueue = new PriorityQueue<Vertex>();
    // Queue<Vertex> vertexQueue = new LinkedList<Vertex>();
    vertexQueue.add(source);

    while (!vertexQueue.isEmpty()) {
        Vertex fromVertex = vertexQueue.poll();

        if (fromVertex.neighbors != null) {
            for (Edge currentEdge : fromVertex.neighbors) {
                Vertex toVertex = currentEdge.target;
                if (currentEdge.weight + fromVertex.minDistance < toVertex.minDistance) {
                    toVertex.minDistance = currentEdge.weight + fromVertex.minDistance;
                    toVertex.previous = fromVertex;
                    vertexQueue.add(toVertex);
                }
            }
        }
    }
}

public static void computeDijkstraBad(Vertex source) {
    source.minDistance = 0.;
    // Queue<Vertex> vertexQueue = new PriorityQueue<Vertex>();
    Queue<Vertex> vertexQueue = new LinkedList<Vertex>();
    vertexQueue.add(source);

    while (!vertexQueue.isEmpty()) {
        Vertex fromVertex = vertexQueue.poll();

        if (fromVertex.neighbors != null) {
            for (Edge currentEdge : fromVertex.neighbors) {
                Vertex toVertex = currentEdge.target;
                if (currentEdge.weight + fromVertex.minDistance < toVertex.minDistance) {
                    toVertex.minDistance = currentEdge.weight + fromVertex.minDistance;
                    toVertex.previous = fromVertex;
                    vertexQueue.add(toVertex);
                }
            }
        }
    }
}

我還使用如下文本文件創建圖形

0, 1, 2, 3, 4, 5, 6 // vertices
0, 6 // from and to vertex
1, (2-5, 0-4, 4-6) // from vertex 1 it will have edge to 2 with weight 5 ...
0, (4-3, 3-7)
4, (2-11, 3-8)
3, (2-2, 5-5)
2, (6-2, 5-10)
5, (6-3)

兩種實現都呈現以下[0, 3, 2, 6] ,這確實是從 0 到 6 的最短路徑!

現在我們知道,如果使用 Simple BFS 來尋找正整數的最短距離,就會出現找不到最小路徑的情況。 那么,有人可以給我一個反例,我的 Bad 實現將無法打印圖表的正確路徑。 隨時以我使用的圖形格式(示例文本文件格式)給我答案。

到目前為止,我所擁有的所有圖表,兩種實現都呈現了正確的結果。 這不應該發生,因為糟糕的實現是運行時 (E+V),我們知道如果沒有至少 E log V,我們就找不到最短路徑。

另一個例子,

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
0, 10
0, (1-9, 2-10, 3-11)
1, (4-1, 5-7)
2, (4-4, 5-3, 6-5)
3, (5-1, 6-4)
4, (7-9, 8-14, 5-3)
5, (7-4, 8-5, 9-9, 6-2)
6, (8-2, 9-2)
7, (10-3)
8, (10-2)
9, (10-5)

兩種實現都渲染 [0, 3, 5, 6, 8, 10],這是從 0-10 的正確最短路徑。

我相信您給出的算法是正確的,但它不如 Dijkstra 的算法有效。

在高層次上,您的算法通過找到一個“活動”節點(距離已降低的節點),然后掃描出邊以激活所有需要更新其距離的相鄰節點來工作。 請注意,同一個節點可以被多次激活——事實上,一個節點可能會在每次其候選距離下降時被激活一次,這在算法的運行中可能會發生多次。 此外,如果候選距離多次下降,您實現的算法可能會將同一個節點多次放入隊列,因此除了第一個之外的所有出隊都可能是不必要的。 總體而言,我預計這會導致大圖的運行時受到很大影響。

從某種意義上說,您實現的算法是最短路徑算法,但它不是 Dijkstra 算法。 主要區別在於,Dijkstra 的算法使用優先級隊列來確保每個節點出隊和處理一次且恰好一次,從而提高了效率。

所以我想我能給出的最好答案是“你的算法不是 Dijkstra 算法的實現,Dijkstra 算法使用優先級隊列的原因是為了避免像你的算法那樣多次重新計算距離。”

你的算法會找到正確的結果,但你的方法正在做的是它扼殺了 Dijkstra 方法的效率。

示例:

考慮 3 個名為 AB C 的節點。

A->C :7 
A->B :2
B->C :3

在您的壞方法中,您首先將 A 到 C 的最短路徑設置為 7,然后在遍歷時將其修改為 5 (A->BC)

在 Dijkstra 的方法中,根本不會遍歷 A->C,因為在使用最小堆時,將首先遍歷 A->B,將 B 標記為“已遍歷”,然后將遍歷 B->C ,並且,C 將被標記為“已遍歷”。 現在,由於 C 已被標記為“已遍歷”,因此永遠不會檢查路徑 A->C(長度為 7)。

因此,如您所見,在您的糟糕方法中,您將到達 C 2 次(A->C & A->B->C),而使用 Dijkstra 的方法時,您將只到達 C 一次。

這個例子應該證明你使用 Dijkstra 算法的迭代次數會更少。

看了別人的答案,他們是對的,總結是不好的實現方法是正確的,但是樓主的說法復雜度(O(E+V))不對。 其他人沒有提到的另一件事,我會在這里添加。

這種糟糕的實現也對應於一種算法,它實際上是 BFS 的衍生物,正式名稱為SPFA 查看SPFA算法 當作者早在 1994 年發表這個算法時,他聲稱它比具有 O(E) 復雜度的 Dijkstra 具有更好的性能,這是錯誤的。

它在 ACM 學生中非常受歡迎。 由於其簡單且易於實施。 至於性能,Dijkstra 是首選。

類似的回復參考這個帖子

所有的答案都寫得很好,所以我不會添加太多解釋。 我正在添加一個示例,這是我在 Striver 在他的一個圖表播放列表視頻中發表的評論中遇到的。 希望這將有助於清楚地看到這個問題。

如果我們在為具有任意邊權重的無向圖實現 Dijkstra 算法時不使用優先隊列,那么我們可能不得不一次又一次地訪問同一個節點,它將無法考慮任意邊權重。

以下面的示例為例,通過優先隊列和簡單隊列進行空運行,您將看到您必須訪問node 6兩次才能進行正常隊列空運行。 因此,在更大的圖表的情況下,這將導致性能非常差。

編輯——5 到 6 個節點的邊權重是 1 而不是 10。我會盡快上傳正確的圖像。

在此處輸入圖像描述

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM