亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

SPFA算法的實現(xiàn)原理及其應用詳解

 更新時間:2023年05月12日 15:01:48   作者:繁依Fanyi  
SPFA算法,全稱為Shortest?Path?Faster?Algorithm,是求解單源最短路徑問題的一種常用算法,本文就來聊聊它的實現(xiàn)原理與簡單應用吧

一、前言

SPFA算法,全稱為Shortest Path Faster Algorithm,是求解單源最短路徑問題的一種常用算法,它可以處理有向圖或者無向圖,邊權可以是正數(shù)、負數(shù),但是不能有負環(huán)。

二、SPFA 算法

1、SPFA算法的基本流程

1. 初始化

首先我們需要起點s到其他頂點的距離初始化為一個很大的值(比如9999999,像是 JAVA 中可以設置 Integer.MAX_VALUE 來使),并將起點s的距離初始化為0。同時,我們還需要將起點s入隊。

2. 迭代

每次從隊列中取出一個頂點u,遍歷所有從u出發(fā)的邊,對于邊(u,v)(其中v為從u可以到達的頂點),如果s->u->v的路徑長度小于s->v的路徑長度,那么我們就更新s->v的路徑長度,并將v入隊。

3. 循環(huán)

不斷進行步驟2,直到隊列為空。

4. 判斷

最后,我們可以得到從起點s到各個頂點的最短路徑長度,如果存在無窮小的距離,則說明從起點s無法到達該頂點。

其次,需要注意的是,SPFA算法中存在負環(huán)問題。如果存在負環(huán),則算法會陷入死循環(huán)。因此,我們需要添加一個計數(shù)器,記錄每個點進隊列的次數(shù)。當一個點進隊列的次數(shù)超過圖中節(jié)點個數(shù)時,就可以判定存在負環(huán)。

2、代碼詳解

以下是使用Java實現(xiàn) SPFA算法的代碼,其中Graph類表示有向圖或無向圖,Vertex類表示圖中的一個頂點,Edge類表示圖中的一條邊。

import java.util.*;
class Graph {   // 圖
    private List<Vertex> vertices;  // 頂點集
    public Graph() {
        vertices = new ArrayList<Vertex>();
    }
    public void addVertex(Vertex v) {   // 添加頂點
        vertices.add(v);
    }   // 添加頂點
    public List<Vertex> getVertices() { // 返回頂點
        return vertices;
    }   // 獲取頂點集
}
class Vertex {  // 點
    private int id; // 點 id
    private List<Edge> edges;   // 連接到該頂點的邊
    private int distance;   // 從源頂點到該頂點的最短距離,MAX_VALUE init
    private boolean visited;    // 在圖的遍歷過程中是否訪問過該頂點,false init
    public Vertex(int id) {
        this.id = id;
        edges = new ArrayList<Edge>();
        distance = Integer.MAX_VALUE;
        visited = false;
    }
    public int getId() {    // 獲取 id
        return id;
    }
    public void addEdge(Edge e) {   // 將連接到該頂點邊添加到列表中
        edges.add(e);
    }   // 添加圖到邊
    public List<Edge> getEdges() {  // 獲取連接到該頂點的邊集
        return edges;
    }   // 獲取圖中邊
    public int getDistance() {  // 獲取從源頂點到該頂點的最短距離
        return distance;
    }   // 獲取源頂點到該頂點的最短距離
    public void setDistance(int distance) { //設置最短距離
        this.distance = distance;
    }   // 設置源頂點到該頂點的最短距離
    public boolean isVisited() {    // 獲取在圖的遍歷過程中是否訪問過該點
        return visited;
    }   // 獲取圖遍歷過程中是否訪問過該點
    public void setVisited(boolean visited) {   // 設置在圖的遍歷過程中是否訪問過該點
        this.visited = visited;
    }   // 設置圖遍歷過程中是否訪問過該點
}
class Edge {    // 邊
    private Vertex source;  // 源頂點
    private Vertex destination; // 目標頂點
    private int weight; // 邊的權重
    public Edge(Vertex source, Vertex destination, int weight) {
        this.source = source;
        this.destination = destination;
        this.weight = weight;
    }
    public Vertex getSource() { // 返回源頂點
        return source;
    }   // 獲取源點
    public Vertex getDestination() {    // 返回目標頂點
        return destination;
    }   // 獲取目標頂點
    public int getWeight() {    // 獲取邊的權重
        return weight;
    }   // 獲取邊的權重
}
// SPFA 算法
public class SPFA { 
    public static void spfa(Graph graph, Vertex source) {
        // 初始化
        Queue<Vertex> queue = new LinkedList<Vertex>(); // 初始化一個頂點隊列,使用該隊列來跟中需要處理的頂點 
        for (Vertex v : graph.getVertices()) {  // 初始化最短距離和是否訪問過該點
            v.setDistance(Integer.MAX_VALUE);
            v.setVisited(false);
        }
        source.setDistance(0); // 將源頂點到自身的最短距離設為0
        queue.add(source);  // 將源頂點添加到隊列中
        // 迭代
        int count = 0;  // 用于檢測圖中的負環(huán),count超過圖中頂點的總數(shù),拋出異常
        // 查找從一個源頂點到圖中所有其它頂點的最短路徑
        while (!queue.isEmpty()) {  
            Vertex u = queue.poll();    // 存儲SPFA算法正在處理的頂點,poll 方法將下一個頂點從隊列中取出
            u.setVisited(false);    // 標記該頂點為未訪問,以便在算法中再次對其處理
            // 查找部分,循環(huán)遍歷當前頂點 u 的所有邊
            for (Edge e : u.getEdges()) {   
                Vertex v = e.getDestination();  // 返回邊 e 的目標頂點給 v
                int distance = u.getDistance() + e.getWeight(); // 計算源頂點到目標頂點的距離
                if (distance < v.getDistance()) {
                    v.setDistance(distance);    // 更新最短距離
                    if (!v.isVisited()) {   // 如果該頂點未被訪問過
                        queue.add(v);   // 將該頂點添加到隊列中
                        v.setVisited(true); // 標記該頂點已被訪問
                        count++;    // 負環(huán) + 1
                        if (count > graph.getVertices().size()) {   // 檢查 SPFA 算法處理的頂點數(shù)是否大于圖中頂點總數(shù)
                            throw new RuntimeException("Negative cycle detected");
                        }
                    }
                }
            }
        }
    }
    public static void main(String[] args) {
        // 構造圖
        Graph graph = new Graph();
        // 構造頂點
        Vertex s = new Vertex(0);
        Vertex a = new Vertex(1);
        Vertex b = new Vertex(2);
        Vertex c = new Vertex(3);
        Vertex d = new Vertex(4);
        // 點加邊
        s.addEdge(new Edge(s, a, 2));
        s.addEdge(new Edge(s, c, 1));
        a.addEdge(new Edge(a, b, 3));
        b.addEdge(new Edge(b, d, 2));
        c.addEdge(new Edge(c, d, 1));
        // 邊加點
        graph.addVertex(s);
        graph.addVertex(a);
        graph.addVertex(b);
        graph.addVertex(c);
        graph.addVertex(d);
        // 調(diào)用SPFA算法求解最短路徑
        spfa(graph, s);
        // 輸出結果
        for (Vertex v :graph.getVertices()) {
            System.out.println("Shortest distance from source to vertex " + v.getId() + " is " + v.getDistance()); 
        } 
    } 
}

上面的代碼實現(xiàn)了SPFA算法,并計算了從給定源點到圖中其他所有頂點的最短路徑。主要思路如下:

  • 初始化:將所有頂點的距離設置為正無窮,將源點的距離設置為0,將源點加入隊列。
  • 迭代:從隊列中取出一個頂點u,遍歷它的所有鄰居v。如果u到源點的距離加上u到v的邊的權重小于v的距離,則更新v的距離,并將v加入隊列中。如果v已經(jīng)在隊列中,則不需要再次添加。
  • 如果隊列為空,則算法結束。如果隊列非空,則回到步驟2。

SPFA算法的時間復雜度取決于負權邊的數(shù)量。如果圖中沒有負權邊,算法的時間復雜度是O(E),其中E是邊的數(shù)量。但是如果圖中有負權邊,算法的時間復雜度將達到O(VE),其中V是頂點的數(shù)量,E是邊的數(shù)量。因此,為了避免算法的時間復雜度變得非常高,應盡可能避免在圖中使用負權邊。

三、SPFA 算法已死

這個問題引發(fā)了很多OI選手和出題人的討論,雖然 SPFA 算法在實際應用中具有一定的優(yōu)勢,但它也有一些缺點,導致它被稱為"已死"的算法之一。以下是幾個原因:

  • 可能會進入負環(huán):SPFA 算法可以處理負權邊,但是如果有負權環(huán),算法將無法結束,因為每次都會沿著負權環(huán)一遍一遍地更新距離,導致算法陷入死循環(huán)。
  • 時間復雜度不穩(wěn)定:在最壞情況下,SPFA 算法的時間復雜度可以達到 O ( V E ) O(VE) O(VE),其中 V V V 和 E E E 分別是圖中的頂點數(shù)和邊數(shù)。而在最好情況下,時間復雜度只有 O ( E ) O(E) O(E)。因此,SPFA 算法的時間復雜度是不穩(wěn)定的。
  • 存在更好的算法:對于單源最短路徑問題,已經(jīng)有更好的算法出現(xiàn),如 Dijkstra 算法和 Bellman-Ford 算法。這些算法在時間復雜度和穩(wěn)定性方面都比 SPFA 算法更優(yōu)秀。

雖然 SPFA 算法在某些情況下可以發(fā)揮出優(yōu)勢,但是它的缺點也是無法忽視的,而且已經(jīng)有更好的算法出現(xiàn),不少大佬也或多或少的對 SPFA 算法進行了優(yōu)化,更多優(yōu)化的內(nèi)容以及最短路徑算法可以在論文中找到。因此,SPFA 算法已經(jīng)不是首選算法,也可以說是已經(jīng)“死亡”了。

到此這篇關于SPFA算法的實現(xiàn)原理及其應用詳解的文章就介紹到這了,更多相關SPFA算法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 老生常談Log4j和Log4j2的區(qū)別(推薦)

    老生常談Log4j和Log4j2的區(qū)別(推薦)

    下面小編就為大家?guī)砝仙U凩og4j和Log4j2的區(qū)別(推薦)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-04-04
  • Java后端之俯瞰數(shù)據(jù)接收的三種方式

    Java后端之俯瞰數(shù)據(jù)接收的三種方式

    在前后端分離的開發(fā)項目中,前后端聯(lián)調(diào)的時候會出現(xiàn)這樣那樣的問題,尤其是在調(diào)取數(shù)據(jù)的程序上面,有時候前端給的前端給到后端的明明是正確的但就是無法拿到正確的數(shù)據(jù),下面小千就來給大家詳解一下常見的三種數(shù)據(jù)傳輸方式
    2021-10-10
  • MyBatis特殊SQL的執(zhí)行實例代碼

    MyBatis特殊SQL的執(zhí)行實例代碼

    這篇文章主要給大家介紹了關于MyBatis特殊SQL執(zhí)行的相關資料,文中通過實例代碼和圖文介紹的非常詳細,對大家學習或者使用MyBatis具有一定的參考學習價值,需要的朋友可以參考下
    2023-01-01
  • 詳解Java中自定義注解的使用

    詳解Java中自定義注解的使用

    Annontation是Java5開始引入的新特征,中文名稱叫注解,它提供了一種安全的類似注釋的機制,用來將任何的信息或元數(shù)據(jù)(metadata)與程序元素(類、方法、成員變量等)進行關聯(lián)。本文主要介紹了自定義注解的使用,希望對大家有所幫助
    2023-03-03
  • Java中線程安全有哪些實現(xiàn)思路

    Java中線程安全有哪些實現(xiàn)思路

    在 Java 多線程編程中,線程安全是一個非常重要的概念,本文主要介紹了Java中線程安全有哪些實現(xiàn)思路,非常具有實用價值,需要的朋友可以參考下
    2023-05-05
  • Java的RocketMQ之消息存儲和查詢原理詳解

    Java的RocketMQ之消息存儲和查詢原理詳解

    這篇文章主要介紹了Java的RocketMQ之消息存儲和查詢原理詳解,一臺Broker服務器只有一個CommitLog文件(組),RocketMQ會將所有主題的消息存儲在同一個文件中,這個文件中就存儲著一條條Message,每條Message都會按照順序寫入,需要的朋友可以參考下
    2024-01-01
  • Java中的@SneakyThrows注解詳解

    Java中的@SneakyThrows注解詳解

    這篇文章主要介紹了Java中的@SneakyThrows注解詳解,@SneakyThrows將當前方法拋出的異常,包裝成RuntimeException,騙過編譯器,使得調(diào)用點可以不用顯示處理異常信息,需要的朋友可以參考下
    2023-10-10
  • Spring Boot配置攔截器及實現(xiàn)跨域訪問的方法

    Spring Boot配置攔截器及實現(xiàn)跨域訪問的方法

    這篇文章主要介紹了Spring Boot配置攔截器及實現(xiàn)跨域訪問的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2018-12-12
  • 詳解如何為SpringBoot Web應用的日志方便追蹤

    詳解如何為SpringBoot Web應用的日志方便追蹤

    在Web應用程序領域,有效的請求監(jiān)控和可追溯性對于維護系統(tǒng)完整性和診斷問題至關重要,SpringBoot是一種用于構建Java應用程序的流行框架,在本文中,我們探討了在SpringBoot中向日志添加唯一ID的重要性,需要的朋友可以參考下
    2023-11-11
  • 總結Java調(diào)用Python程序方法

    總結Java調(diào)用Python程序方法

    這篇文章主要介紹了總結Java調(diào)用Python程序方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-08-08

最新評論