最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SPFA算法的實(shí)現(xiàn)原理及其應(yīng)用詳解

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

一、前言

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

二、SPFA 算法

1、SPFA算法的基本流程

1. 初始化

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

2. 迭代

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

3. 循環(huán)

不斷進(jìn)行步驟2,直到隊(duì)列為空。

4. 判斷

最后,我們可以得到從起點(diǎn)s到各個(gè)頂點(diǎn)的最短路徑長(zhǎng)度,如果存在無(wú)窮小的距離,則說(shuō)明從起點(diǎn)s無(wú)法到達(dá)該頂點(diǎn)。

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

2、代碼詳解

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

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

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

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

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

三、SPFA 算法已死

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

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

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

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

相關(guān)文章

  • idea文件被鎖無(wú)法更改問(wèn)題

    idea文件被鎖無(wú)法更改問(wèn)題

    這篇文章主要介紹了idea文件被鎖無(wú)法更改問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • 解決MyBatis中為類配置別名,列名與屬性名不對(duì)應(yīng)的問(wèn)題

    解決MyBatis中為類配置別名,列名與屬性名不對(duì)應(yīng)的問(wèn)題

    這篇文章主要介紹了解決MyBatis中為類配置別名,列名與屬性名不對(duì)應(yīng)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-11-11
  • Springboot 整合通用mapper和pagehelper展示分頁(yè)數(shù)據(jù)的問(wèn)題(附github源碼)

    Springboot 整合通用mapper和pagehelper展示分頁(yè)數(shù)據(jù)的問(wèn)題(附github源碼)

    這篇文章主要介紹了Springboot 整合通用mapper和pagehelper展示分頁(yè)數(shù)據(jù)(附github源碼),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-09-09
  • Java中SpringBoot的TCC事務(wù)詳解

    Java中SpringBoot的TCC事務(wù)詳解

    這篇文章主要介紹了Java中SpringBoot的TCC事務(wù)詳解,近年來(lái),隨著微服務(wù)架構(gòu)的普及,TCC?事務(wù)成為了一種非常流行的分布式事務(wù)解決方案,在?Spring?Boot?中,我們可以很容易地使用?TCC?事務(wù)來(lái)管理分布式事務(wù),需要的朋友可以參考下
    2023-07-07
  • MyBatisPlus中CRUD使用方法詳解

    MyBatisPlus中CRUD使用方法詳解

    通用CRUD封裝,BaseMapper(opens new window)接口,為Mybatis-Plus啟動(dòng)時(shí)自動(dòng)解析實(shí)體表關(guān)系映射轉(zhuǎn)換為Mybatis內(nèi)部對(duì)象注入容器
    2023-01-01
  • java解一個(gè)比較特殊的數(shù)組合并題

    java解一個(gè)比較特殊的數(shù)組合并題

    這篇文章主要介紹了java解一個(gè)比較特殊的數(shù)組合并題,需要的朋友可以參考下
    2014-06-06
  • restemplate請(qǐng)求亂碼之content-encoding=“gzip“示例詳解

    restemplate請(qǐng)求亂碼之content-encoding=“gzip“示例詳解

    RestTemplate從Spring3.0開(kāi)始支持的一個(gè)HTTP請(qǐng)求工具,它提供了常見(jiàn)的REST請(qǐng)求方案的模板,及一些通用的請(qǐng)求執(zhí)行方法 exchange 以及 execute,接下來(lái)通過(guò)本文給大家介紹restemplate請(qǐng)求亂碼之content-encoding=“gzip“,需要的朋友可以參考下
    2024-03-03
  • Springboot之@Controller注解不生效問(wèn)題及解決

    Springboot之@Controller注解不生效問(wèn)題及解決

    這篇文章主要介紹了Springboot之@Controller注解不生效問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • spring的@Transactional注解用法解讀

    spring的@Transactional注解用法解讀

    這篇文章主要介紹了spring的@Transactional注解用法解讀,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-01-01
  • Spring中的BeanDefinition注冊(cè)流程詳解

    Spring中的BeanDefinition注冊(cè)流程詳解

    這篇文章主要介紹了Spring中的BeanDefinition注冊(cè)流程詳解,  NamespaceHandler簡(jiǎn)單來(lái)說(shuō)就是命名空間處理器,Spring為了開(kāi)放性提供了NamespaceHandler機(jī)制,這樣我們就可以根據(jù)需求自己來(lái)處理我們?cè)O(shè)置的標(biāo)簽元素,需要的朋友可以參考下
    2023-12-12

最新評(píng)論

河池市| 新乡县| 深水埗区| 乐至县| 安平县| 新昌县| 皮山县| 徐州市| 兴和县| 洮南市| 弋阳县| 彭山县| 永寿县| 高尔夫| 静海县| 通渭县| 荥经县| 怀集县| 焦作市| 阜阳市| 济南市| 保德县| 那曲县| 安溪县| 凤翔县| 云安县| 虹口区| 祥云县| 扎兰屯市| 保靖县| 宁蒗| 阜南县| 沐川县| 闻喜县| 筠连县| 屯留县| 衡东县| 吐鲁番市| 新邵县| 阿图什市| 宜黄县|