最短路徑問題----Dijkstra算法的解釋


先上圖:

現在要找到地點V1到其余各個地點的最短路徑(圖中數字的單位默認為km.)。有一個原則是:永遠找最小,確保無更小。

第一步:v1->v1,v1->v2,...v1->v7的距離用一維數組dis[0],dis[1],dis[2],...dis[6]表示。初始化數組:dis=[0 50 inf inf 30 inf inf];

第二步:找到dis數組中的最小值(注意不算v1->v1=0),它是dis[4]=30;這個距離就是v1->v5的最小距離了,因為所有的距離都是正數,如果你從v1出發,通過其他頂點繞路繞到v5,那總距離肯定大於30,因為30+(一個大於0的數)肯定大於30.此時,找到了v1->v5的最短路徑;

第三步:把頂點v5置為true,代表頂點v5已被訪問;

第四步:看v5通往里,從圖上可以看到v5通往v3和v7,v5->v3的距離是60,v5->v7的距離是120,所以通過v5中轉到v3和v7后,v1->v3的距離變成30+60=90,v1->v7的距離變成30+120=150;這對dis數組中保存的v1到其它各個頂點的距離產生了影響, 此時就用更小的值去更新dis數組中的值。更新后的dis數組是:dis=[0 50 90 inf 30 inf 150].(注:inf代表無窮大)

第五步:找dis數組中除去v1->v5=30這個值之后的最小值,它是v1->v2=50,找到后把頂點v2置為ture.

第六步:從v1出發,通過v2中轉,看v2會到達哪里。可以看到,v2->v3=20,這時更新dis數組中v1->v3值,把90更新成70.此時數組dis=[0 50 70 inf 30 inf 150].

第七步:重復第5步,找排除掉頂點是ture的值后,剩余的數的最小值,它是v1->v3=70,把頂點v3置為true.

第八步:從v1出發,通過v3中轉,看v3會到達哪里。可以看到,v3->v7=10,這時更新dis數組中v1->v7值,把150更新成80.此時數組dis=[0 50 70 inf 30 inf 80].

第九步:結束!

最終結果:

 

以下C++代碼來源於:https://blog.csdn.net/qq_35644234/article/details/60870719

 Dijkstra.h文件的代碼:

/************************************************************/
/*                程序作者:Willam                          */
/*                程序完成時間:2017/3/8                    */
/*                有任何問題請聯系:2930526477@qq.com       */
/************************************************************/
//@盡量寫出完美的程序

#pragma once
//#pragma once是一個比較常用的C/C++雜注,
//只要在頭文件的最開始加入這條雜注,
//就能夠保證頭文件只被編譯一次。

#include<iostream>
#include<string>
using namespace std;

/*
本程序是使用Dijkstra算法實現求解最短路徑的問題
采用的鄰接矩陣來存儲圖
*/
//記錄起點到每個頂點的最短路徑的信息
struct Dis {
    string path;
    int value;
    bool visit;
    Dis() {
        visit = false;
        value = 0;
        path = "";
    }
};

class Graph_DG {
private:
    int vexnum;   //圖的頂點個數
    int edge;     //圖的邊數
    int **arc;   //鄰接矩陣
    Dis * dis;   //記錄各個頂點最短路徑的信息
public:
    //構造函數
    Graph_DG(int vexnum, int edge);
    //析構函數
    ~Graph_DG();
    // 判斷我們每次輸入的的邊的信息是否合法
    //頂點從1開始編號
    bool check_edge_value(int start, int end, int weight);
    //創建圖
    void createGraph();
    //打印鄰接矩陣
    void print();
    //求最短路徑
    void Dijkstra(int begin);
    //打印最短路徑
    void print_path(int);
};
View Code

Dijkstra.cpp文件的代碼:

#include"Dijkstra.h"

//構造函數
Graph_DG::Graph_DG(int vexnum, int edge) {
    //初始化頂點數和邊數
    this->vexnum = vexnum;//this 代表調用構造函數的那一個一個對象的地址
    this->edge = edge;
    //為鄰接矩陣開辟空間和賦初值
    arc = new int*[this->vexnum];//arc 是二級指針
    dis = new Dis[this->vexnum];
    for (int i = 0; i < this->vexnum; i++) {
        arc[i] = new int[this->vexnum];
        for (int k = 0; k < this->vexnum; k++) {
            //鄰接矩陣初始化為無窮大
            arc[i][k] = INT_MAX;
        }
    }
}
//析構函數
Graph_DG::~Graph_DG() {
    delete[] dis;
    for (int i = 0; i < this->vexnum; i++) {
        delete this->arc[i];
    }
    delete arc;
}

// 判斷我們每次輸入的的邊的信息是否合法
//頂點從1開始編號
bool Graph_DG::check_edge_value(int start, int end, int weight) {
    if (start<1 || end<1 || start>vexnum || end>vexnum || weight < 0) {
        return false;
    }
    return true;
}

void Graph_DG::createGraph() {
    cout << "請輸入每條邊的起點和終點(頂點編號從1開始)以及其權重" << endl;
    int start;
    int end;
    int weight;
    int count = 0;
    while (count != this->edge) {
        cin >> start >> end >> weight;
        //首先判斷邊的信息是否合法
        while (!this->check_edge_value(start, end, weight)) {
            cout << "輸入的邊的信息不合法,請重新輸入" << endl;
            cin >> start >> end >> weight;
        }
        //對鄰接矩陣對應上的點賦值
        arc[start - 1][end - 1] = weight;
        //無向圖添加上這行代碼
        //arc[end - 1][start - 1] = weight;
        ++count;
    }
}

void Graph_DG::print() {
    cout << "圖的鄰接矩陣為:" << endl;
    int count_row = 0; //打印行的標簽
    int count_col = 0; //打印列的標簽
    //開始打印
    while (count_row != this->vexnum) {
        count_col = 0;
        while (count_col != this->vexnum) {
            if (arc[count_row][count_col] == INT_MAX)
                cout << "" << " ";
            else
                cout << arc[count_row][count_col] << " ";
            ++count_col;
        }
        cout << endl;
        ++count_row;
    }
}
void Graph_DG::Dijkstra(int begin){
    //首先初始化我們的dis數組
    int i;
    for (i = 0; i < this->vexnum; i++) {
        //設置當前的路徑
        dis[i].path = "v" + to_string(begin) + "-->v" + to_string(i + 1);
        dis[i].value = arc[begin - 1][i];
    }
    //設置起點的到起點的路徑為0
    dis[begin - 1].value = 0;
    dis[begin - 1].visit = true;

    int count = 1;
    //計算剩余的頂點的最短路徑(剩余this->vexnum-1個頂點)
    while (count != this->vexnum) {
        //temp用於保存當前dis數組中最小的那個下標
        //min記錄的當前的最小值
        int temp = 0;
        int min = INT_MAX;
        for (i = 0; i < this->vexnum; i++) {
            if (!dis[i].visit && dis[i].value<min) {
                min = dis[i].value;
                temp = i;
            }
        }
        //cout << temp + 1 << "  "<<min << endl;
        //把temp對應的頂點加入到已經找到的最短路徑的集合中
        dis[temp].visit = true;
        ++count;
        for (i = 0; i < this->vexnum; i++) {
            //注意這里的條件arc[temp][i]!=INT_MAX必須加,不然會出現溢出,從而造成程序異常
            if (!dis[i].visit && arc[temp][i] != INT_MAX && (dis[temp].value + arc[temp][i]) < dis[i].value) {
                //如果新得到的邊可以影響其他為訪問的頂點,那就更新它的最短路徑和長度
                dis[i].value = dis[temp].value + arc[temp][i];
                dis[i].path = dis[temp].path + "-->v" + to_string(i + 1);
            }
        }
    }

}
void Graph_DG::print_path(int begin) {
    string str;
    str = "v" + to_string(begin);
    cout << "" << str << "為起點的圖的最短路徑為:" << endl;
    for (int i = 0; i != this->vexnum; i++) {
        if (dis[i].value != INT_MAX)
            cout << dis[i].path << "=" << dis[i].value << endl;
        else {
            cout << dis[i].path << "是無最短路徑的" << endl;
        }
    }
}
View Code

main.cpp文件的代碼:

#include"Dijkstra.h"


//檢驗輸入邊數和頂點數的值是否有效,可以自己推算為啥:
//頂點數和邊數的關系是:((Vexnum*(Vexnum - 1)) / 2) < edge
bool check(int Vexnum, int edge) {
    if (Vexnum <= 0 || edge <= 0 || ((Vexnum*(Vexnum - 1)) / 2) < edge)
        return false;
    return true;
}
int main() {
    int vexnum; int edge;

    cout << "輸入圖的頂點個數和邊的條數:" << endl;
    cin >> vexnum >> edge;
    while (!check(vexnum, edge)) {
        cout << "輸入的數值不合法,請重新輸入" << endl;
        cin >> vexnum >> edge;
    }
    Graph_DG graph(vexnum, edge);
    graph.createGraph();
    graph.print();
    graph.Dijkstra(1);
    graph.print_path(1);
    system("pause");
    return 0;
}
View Code

運行結果:

 


 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM