欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解Dijkstra算法原理及其C++實(shí)現(xiàn)

 更新時(shí)間:2022年07月15日 16:43:19   作者:卡爾曼和玻爾茲曼誰(shuí)曼  
Dijkstra算法用于計(jì)算一個(gè)節(jié)點(diǎn)到其他節(jié)點(diǎn)的最短路徑。Dijkstra是一種按路徑長(zhǎng)度遞增的順序逐步產(chǎn)生最短路徑的方法,是一種貪婪算法。本文將詳解Dijkstra算法原理及其C++實(shí)現(xiàn),感興趣的可以了解一下

什么是最短路徑問(wèn)題

如果從圖中某一頂點(diǎn)(稱(chēng)為源點(diǎn))到達(dá)另一頂點(diǎn)(稱(chēng)為終點(diǎn))的路徑可能不止一條,如何找到一條路徑使得沿此路徑上各邊上的權(quán)值總和達(dá)到最小。

單源最短路徑問(wèn)題是指對(duì)于給定的圖G=(V,E),求源點(diǎn)v0到其它頂點(diǎn)vt的最短路徑。

Dijkstra算法

Dijkstra算法用于計(jì)算一個(gè)節(jié)點(diǎn)到其他節(jié)點(diǎn)的最短路徑。Dijkstra是一種按路徑長(zhǎng)度遞增的順序逐步產(chǎn)生最短路徑的方法,是一種貪婪算法。

Dijkstra算法的核心思想是首先求出長(zhǎng)度最短的一條最短路徑,再參照它求出長(zhǎng)度次短的一條最短路徑,依次類(lèi)推,直到從源點(diǎn)v0到其它各頂點(diǎn)的最短路徑全部求出為止。

具體來(lái)說(shuō):圖中所有頂點(diǎn)分成兩組,第一組是已確定最短路徑的頂點(diǎn),初始只包含一個(gè)源點(diǎn),記為集合S;第二組是尚未確定最短路徑的頂點(diǎn),記為集合U。

按最短路徑長(zhǎng)度遞增的順序逐個(gè)把U中的頂點(diǎn)加到S中去,同時(shí)動(dòng)態(tài)更新U集合中源點(diǎn)到各個(gè)頂點(diǎn)的最短距離,直至所有頂點(diǎn)都包括到S中。

實(shí)現(xiàn)思路

1.初始時(shí),S集合只包含起點(diǎn)v0;U集合包含除v0外的其他頂點(diǎn)vt,且U中頂點(diǎn)的距離為起點(diǎn)v0到該頂點(diǎn)的距離。(U 中頂點(diǎn)vt的距離為(v0,vt)的長(zhǎng)度,如果v0和vt不相鄰,則vt的最短距離為∞)

2.從U中選出距離最短的頂點(diǎn)vt′,并將頂點(diǎn)vt′加入到S中;同時(shí),從U中移除頂點(diǎn)vt′。

3.更新U中各個(gè)頂點(diǎn)vt到起點(diǎn)v0的距離以及最短路徑中當(dāng)前頂點(diǎn)的前驅(qū)頂點(diǎn)。之所以更新U中頂點(diǎn)的距離以及前驅(qū)頂點(diǎn)是由于上一步中確定了vt′是求出最短路徑的頂點(diǎn),從而可以利用vt′來(lái)更新U中其它頂點(diǎn)vt的距離,因?yàn)榇嬖?v0,vt)的距離可能大于(v0,vt')+(vt',vt)距離的情況,從而也需要更新其前驅(qū)頂點(diǎn)

4.重復(fù)步驟(2)和(3),直到遍歷完所有頂點(diǎn)

案例分析

代碼實(shí)現(xiàn)

使用了部分C++11特性,注釋豐富,讀起來(lái)應(yīng)該不會(huì)太困難!

#include <cstdio>
#include <iostream>
#include <vector>
#include <list>
#include <stack>

using namespace std;
using Matrix = vector<vector<uint>>;                // 連接矩陣(使用嵌套的vector表示)
using SNodes = vector<tuple<uint, uint, uint>>;     // 已計(jì)算出最短路徑的頂點(diǎn)集合S(類(lèi)似一個(gè)動(dòng)態(tài)數(shù)組)
using UNodes = list<tuple<uint, uint, uint>>;       // 未進(jìn)行遍歷的頂點(diǎn)集合U(使用list主要是方便元素刪除操作)
using ENode = tuple<uint, uint, uint>;              // 每個(gè)節(jié)點(diǎn)包含(頂點(diǎn)編號(hào),當(dāng)前頂點(diǎn)到起始點(diǎn)最短距離,最短路徑中當(dāng)前頂點(diǎn)的上一個(gè)頂點(diǎn))信息


/***
 * 從未遍歷的U頂點(diǎn)集合中找到下一個(gè)離起始頂點(diǎn)距離最短的頂點(diǎn)
 * @param unvisitedNodes 未遍歷的U頂點(diǎn)集合
 * 每個(gè)元素是(頂點(diǎn)編號(hào),當(dāng)前頂點(diǎn)到起始點(diǎn)最短距離,最短路徑中當(dāng)前頂點(diǎn)的上一個(gè)頂點(diǎn))的tuple
 * @return 下一個(gè)離起始頂點(diǎn)距離最短的頂點(diǎn)
 */
ENode searchNearest(const UNodes &unvisitedNodes) {
    uint minDistance = UINT_MAX;
    ENode nearest;
    for (const auto &node: unvisitedNodes) {
        if (get<1>(node) <= minDistance) {
            minDistance = get<1>(node);
            nearest = node;
        }
    }
    return nearest;
}


/***
 * 迪克斯特拉算法的實(shí)現(xiàn)
 * @param graph 連接矩陣(使用嵌套的vector表示)
 * @param startNodeIndex 起始點(diǎn)編碼(從0開(kāi)始)
 * @return 返回一個(gè)vector,每個(gè)元素是到起始頂點(diǎn)的距離排列的包含(頂點(diǎn)編號(hào),當(dāng)前頂點(diǎn)到起始點(diǎn)最短距離,最短路徑中當(dāng)前頂點(diǎn)的上一個(gè)頂點(diǎn))的tuple
 */
SNodes dijkstra(const Matrix &graph, uint startNodeIndex) {
    const uint numOfNodes = graph.size();   // 圖中頂點(diǎn)的個(gè)數(shù)
    // S是已計(jì)算出最短路徑的頂點(diǎn)的集合(頂點(diǎn)編號(hào),當(dāng)前頂點(diǎn)到起始點(diǎn)最短距離,最短路徑中當(dāng)前頂點(diǎn)的上一個(gè)頂點(diǎn))
    SNodes visitedNodes;
    // U是未計(jì)算出最短路徑的頂點(diǎn)的集合(其中的key為頂點(diǎn)編號(hào),value為到起始頂點(diǎn)最短距離和最短路徑中上一個(gè)節(jié)點(diǎn)編號(hào)組成的pair)
    UNodes unvisitedNodes;

    // 對(duì)S和U集合進(jìn)行初始化,起始頂點(diǎn)的距離為0,其他頂點(diǎn)的距離為無(wú)窮大
    // 最短路徑中當(dāng)前頂點(diǎn)的上一個(gè)頂點(diǎn)初始化為起始頂點(diǎn),后面會(huì)逐步進(jìn)行修正
    for (auto i = 0; i < numOfNodes; ++i) {
        if (i == startNodeIndex) visitedNodes.emplace_back(i, 0, startNodeIndex);
        else unvisitedNodes.emplace_back(i, graph[startNodeIndex][i], startNodeIndex);
    }

    while (!unvisitedNodes.empty()) {
        // 從U中找到距離起始頂點(diǎn)距離最短的頂點(diǎn),加入S,同時(shí)從U中刪除
        auto nextNode = searchNearest(unvisitedNodes);
        unvisitedNodes.erase(find(unvisitedNodes.begin(), unvisitedNodes.end(), nextNode));
        visitedNodes.emplace_back(nextNode);
        // 更新U集合中各個(gè)頂點(diǎn)的最短距離以及最短路徑中的上一個(gè)頂點(diǎn)
        for (auto &node: unvisitedNodes) {
            // 更新的判斷依據(jù)就是起始頂點(diǎn)到當(dāng)前頂點(diǎn)(nextNode)距離加上當(dāng)前頂點(diǎn)到U集合中頂點(diǎn)的距離小于原來(lái)起始頂點(diǎn)到U集合中頂點(diǎn)的距離
            // 更新最短距離的時(shí)候同時(shí)需要更新最短路徑中的上一個(gè)頂點(diǎn)為nextNode
            if (graph[get<0>(nextNode)][get<0>(node)] != UINT_MAX &&
                graph[get<0>(nextNode)][get<0>(node)] + get<1>(nextNode) < get<1>(node)) {
                get<1>(node) = graph[get<0>(nextNode)][get<0>(node)] + get<1>(nextNode);
                get<2>(node) = get<0>(nextNode);
            }
        }
    }

    return visitedNodes;
}


/***
 * 對(duì)使用迪克斯特拉算法求解的最短路徑進(jìn)行打印輸出
 * @param paths vector表示的最短路徑集合
 * 每個(gè)元素是到起始頂點(diǎn)的距離排列的包含(頂點(diǎn)編號(hào),當(dāng)前頂點(diǎn)到起始點(diǎn)最短距離,最短路徑中當(dāng)前頂點(diǎn)的上一個(gè)頂點(diǎn))的tuple
 */
void print(const SNodes &paths) {
    stack<int> tracks;  //從尾部出發(fā),使用stack將每個(gè)頂點(diǎn)的最短路徑中的前一個(gè)頂點(diǎn)入棧,然后出棧的順序就是最短路徑順序
    // 第一個(gè)元素是起始點(diǎn),從第二個(gè)元素進(jìn)行打印輸出
    for (auto it = ++paths.begin(); it != paths.end(); ++it) {
        // 打印頭部信息
        printf("%c -> %c:\t Length: %d\t Paths: %c",
               char(get<0>(paths[0]) + 65),
               char(get<0>(*it) + 65),
               get<1>(*it),
               char(get<0>(paths[0]) + 65));
        auto pointer = *it;
        // 如果當(dāng)前指針pointer指向的節(jié)點(diǎn)有中途節(jié)點(diǎn)(判斷的條件是最短路徑中的前一個(gè)節(jié)點(diǎn)不是起始點(diǎn))
        while (get<2>(pointer) != get<0>(paths[0])) {
            tracks.push(get<0>(pointer));
            // Lambda表達(dá)式,使用find_if函數(shù)把當(dāng)前頂點(diǎn)的前一個(gè)頂點(diǎn)從paths中找出來(lái)繼續(xù)進(jìn)行循環(huán)直到前一個(gè)節(jié)點(diǎn)就是起始點(diǎn)
            auto condition = [pointer](tuple<uint, uint, uint> x) { return get<0>(x) == get<2>(pointer); };
            pointer = *find_if(paths.begin(), paths.end(), condition);
        }
        tracks.push(get<0>(pointer));

        // 以出棧的順序進(jìn)行打印輸出
        while (!tracks.empty()) {
            printf(" -> %c", char(tracks.top() + 65));
            tracks.pop();
        }
        printf("\n");
    }
}

int main() {
    Matrix graph = {
            {0,        12,       UINT_MAX, UINT_MAX, UINT_MAX, 16, 14},
            {12,       0,        10,       UINT_MAX, UINT_MAX, 7, UINT_MAX},
            {UINT_MAX, 10,       0, 3,               5,        6, UINT_MAX},
            {UINT_MAX, UINT_MAX, 3, 0,               4, UINT_MAX, UINT_MAX},
            {UINT_MAX, UINT_MAX, 5, 4,               0,        2,  8},
            {16,       7,        6,        UINT_MAX, 2,        9,  9},
            {14,       UINT_MAX, UINT_MAX, UINT_MAX, 8,        9,  0}
    };  // 圖對(duì)應(yīng)的連接矩陣
    auto results = dijkstra(graph, uint('D' - 65));          // 選取頂點(diǎn)C(大寫(xiě)字母A的ASCII編碼是65)
    print(results);     // 打印輸出結(jié)果
    return 0;
}

運(yùn)行結(jié)果:

D -> C:     Length: 3     Paths: D -> C
D -> E:     Length: 4     Paths: D -> E
D -> F:     Length: 6     Paths: D -> E -> F
D -> G:     Length: 12     Paths: D -> E -> G
D -> B:     Length: 13     Paths: D -> C -> B
D -> A:     Length: 22     Paths: D -> E -> F -> A

以上就是詳解Dijkstra算法原理及其C++實(shí)現(xiàn)的詳細(xì)內(nèi)容,更多關(guān)于C++ Dijkstra算法的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C++中的重載、覆蓋、隱藏介紹

    C++中的重載、覆蓋、隱藏介紹

    這篇文章主要介紹了C++中的重載、覆蓋、隱藏介紹,需要的朋友可以參考下
    2015-04-04
  • 深入理解數(shù)組指針與指針數(shù)組的區(qū)別

    深入理解數(shù)組指針與指針數(shù)組的區(qū)別

    本篇文章是對(duì)數(shù)組指針與指針數(shù)組的區(qū)別進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • C++中關(guān)于set刪除的一些坑

    C++中關(guān)于set刪除的一些坑

    這篇文章主要介紹了C++中關(guān)于set刪除的一些坑,因?yàn)檫@個(gè)問(wèn)題浪費(fèi)了很多的時(shí)間,所以想著分享出來(lái)給大家,方便同樣遇到這個(gè)問(wèn)題的朋友們,有需要的朋友們下面來(lái)一起看看吧。
    2017-02-02
  • c++ 數(shù)據(jù)結(jié)構(gòu)map的使用詳解

    c++ 數(shù)據(jù)結(jié)構(gòu)map的使用詳解

    這篇文章主要介紹了c++ 數(shù)據(jù)結(jié)構(gòu)map的使用詳解,幫助大家更好的理解和學(xué)習(xí)使用c++,感興趣的朋友可以了解下
    2021-04-04
  • 深入理解Qt信號(hào)槽機(jī)制

    深入理解Qt信號(hào)槽機(jī)制

    信號(hào)槽是 Qt 框架引以為豪的機(jī)制之一。本文主要介紹了Qt信號(hào)槽機(jī)制,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-06-06
  • C++ continue和break語(yǔ)句

    C++ continue和break語(yǔ)句

    這篇文章主要介紹了C++ continue和break語(yǔ)句,文章圍繞continue和break語(yǔ)句的相關(guān)資料展開(kāi)詳細(xì)內(nèi)容,需要的朋友可以參考一下,希望對(duì)大家有所幫助
    2021-11-11
  • C語(yǔ)言SetConsoleTextAttribute函數(shù)使用方法

    C語(yǔ)言SetConsoleTextAttribute函數(shù)使用方法

    這篇文章介紹了C語(yǔ)言SetConsoleTextAttribute函數(shù)的使用方法,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-12-12
  • 詳細(xì)解析C語(yǔ)言中的開(kāi)方實(shí)現(xiàn)

    詳細(xì)解析C語(yǔ)言中的開(kāi)方實(shí)現(xiàn)

    這篇文章主要介紹了詳細(xì)解析C語(yǔ)言中的開(kāi)方實(shí)現(xiàn),包括一道要求精度的整數(shù)開(kāi)方的題目,需要的朋友可以參考下
    2015-08-08
  • C++事件驅(qū)動(dòng)型銀行排隊(duì)模擬

    C++事件驅(qū)動(dòng)型銀行排隊(duì)模擬

    這篇文章主要為大家詳細(xì)介紹了C++事件驅(qū)動(dòng)型銀行排隊(duì)模擬,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-09-09
  • C語(yǔ)言通訊錄管理系統(tǒng)完整代碼

    C語(yǔ)言通訊錄管理系統(tǒng)完整代碼

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言通訊錄管理系統(tǒng)完整代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08

最新評(píng)論