最完整+全解析的Floyd演算法(C++版)
阿新 • • 發佈:2021-02-18
Floyd演算法(完整版解決最短路徑問題)
本文小述:本文運用鄰接矩陣構造的是有向圖,用鄰接矩陣實現Floyd演算法(有興趣的話可以自己動手用鄰接表的方法嘗試實現以下),在實現的過程中加強了動態陣列的運用。該程式碼配合 B站視訊的講解來看更易懂哦~程式碼是多註釋、完整版(將.cpp和.h的檔案程式碼聯合起來便可完整實現)。
一、Floyd演算法簡介
Floyd(弗洛伊德)演算法相對於Dijkstra演算法來說,可以解決多源最短路徑問題(即可以從任意一個點到任意一個點),可應用於地圖導航走最短路徑、為各城市修建最短路徑的通訊網(節省成本)等問題,時間複雜度是O(n3
二、程式碼部分
知識儲備:動態建立二維陣列、鄰接矩陣、圖論、虛解構函式、靜態函式(static)的運用
Graph.h檔案程式碼
#ifndef GRAPH_H
#define GRAPH_H
#include <algorithm>
#include <stdio.h> // exit()函式需要用到的標頭檔案
#define X 9999 //相當於無窮大
class Graph
{
private:
static Graph* instance; //建立例項:類是一個抽象類,例項可方便用於調取類成員的方法函式
int n, m; //n是頂點個數,m是邊數
char* data; //頂點陣列,用來儲存頂點(char型別)
int** w; //weight 邊的權重,鄰接矩陣
int** path; //用來記錄最小邊權值頂點的序號,鄰接矩陣
public:
Graph();
virtual ~Graph(); //虛解構函式,用來程式結束後釋放new的記憶體
static Graph* getInstance(); //獲取例項
void createGraph(Graph& G);
int getIndex(const Graph& G, char v); //獲取頂點v的在頂點陣列data中的下標
void Floyd(Graph& G);
void showPath(const Graph& G, int u, int v); //展示最短路徑
};
#endif // GRAPH_H
自己在嘗試寫程式碼的時候,要弄清除一個圖有哪些元素、一個演算法需具備哪些元素,才有思路、好下手寫程式碼喔~
Graph.cpp檔案程式碼
#include "Graph.h"
#include <iostream>
using namespace std;
Graph::Graph() //預設構造
{
//ctor
}
Graph* Graph::instance = nullptr; //固定套路
Graph* Graph::getInstance() //同上,記住就好,也可以自己嘗試理解下
{
if(!instance) instance = new Graph();
return instance;
}
int Graph::getIndex(const Graph& G, char v) //獲取頂點v在頂點陣列中的下標
{
for(int i = 0; i < G.n; i++)
if(G.data[i] == v) return i;
return -1; //沒找到就返回-1
}
void Graph::createGraph(Graph& G)
{
cout << "please input the number of vertex and arc:";
cin >> G.n >> G.m;
G.data = new char[G.n]; //動態建立一維陣列
cout << "please input the value of vertice:";
for(int p = 0; p < G.n; p++)
cin >> G.data[p];
char v1, v2;
int power, i, j;
G.w = new int*[G.n]; //動態建立二維陣列,申請了 int* 型別的G.n行空間
for(int s = 0; s < G.n; s++)
G.w[s] = new int[G.n]; //每一行申請一個int型別的G.n列空間
for(int x = 0; x < G.n; x++)
for(int y = 0; y < G.n; y++){
if(x == y) G.w[x][y] = 0; //邊的鄰接矩陣中左對角線權重(即自己的權重)都設為0,因為是多源的
else G.w[x][y] = X; //其他邊的權重初始化為無窮大
}
cout << "please input the weight of arc between 2 vertice as 100 A B:" << endl;
for(int k = 0; k < G.m; k++){
cin >> power >> v1 >> v2;
i = getIndex(G, v1);
j = getIndex(G, v2);
if(i == -1 || j == -1){ //沒在頂點陣列中找到對應的頂點下標
cout << "Sorry, I can't find the vertex" << endl;
exit(-1); //直接退出程式
}
G.w[i][j] = power; //有向圖賦值邊的權重
}
}
void Graph::Floyd(Graph& G)
{
G.path = new int*[G.n]; //動態建立二維陣列
for(int s = 0; s < G.n; s++){
G.path[s] = new int[G.n];
for(int t = 0; t < G.n; t++)
G.path[s][t] = -1; //初始化path鄰接矩陣的值
}
//特別注意:不能用fill函式來初始化動態二維陣列,因為動態new出來的空間不一定連續
for(int v = 0; v < G.n; ++v) //v是指在某兩個點中,它們之間點的下標
for(int i = 0; i < G.n; ++i)
for(int j = 0; j < G.n; ++j)
if(G.w[i][j] > G.w[i][v] + G.w[v][j]) //看配合B站視訊講解效果更棒,這裡不多做解釋!
{
G.w[i][j] = G.w[i][v] + G.w[v][j];
G.path[i][j] = v;
}
}
void Graph::showPath(const Graph& G, int u, int v)
{ //看配合B站視訊講解效果更棒,該函式不多做解釋!
if(G.path[u][v] == -1) cout << G.data[u] << " to " << G.data[v] << endl; //B站輸出的是頂點序號,我這輸出的是頂點的值
else{
int mid = G.path[u][v];
showPath(G, u, mid);
showPath(G, mid, v);
}
}
Graph::~Graph() //虛解構函式作用:一般都是用來程式結束後釋放new出來的記憶體
{
delete[] data;
for(int i = 0; i < n; i++)
delete[] w[i];
delete[] w;
for(int i = 0; i < n; i++)
delete[] path[i];
delete[] path;
}
其實我們在寫演算法的時候,無非就是對資料的儲存進行操作(對二維陣列的操作),這也是演算法的本質。
main.cpp檔案程式碼
#include <iostream>
#include "Graph.h" //自己寫的標頭檔案要用引號
using namespace std;
int main()
{
char v1, v2;
int a, b;
Graph G;
Graph::getInstance()->createGraph(G); //用例項來調取抽象類的函式方法
Graph::getInstance()->Floyd(G);
cout << "please input which two vertice you want to show the shortest path between them:";
cin >> v1 >> v2;
a = Graph::getInstance()->getIndex(G, v1);
b = Graph::getInstance()->getIndex(G, v2);
Graph::getInstance()->showPath(G, a, b);
return 0;
}
例子展示
Input(點我可進入連結