圖論系列文章
前言
再學習了鄰接矩陣與鄰接表之後,我們立刻迎來來圖的應用:求兩頂點之間的最小距離。
其中最爲重要的有兩種算法:
- Djikstra算法 只能求出一個頂點到其他所有頂點的最短距離
- Floyd算法 能求解出任意兩點之間的最小距離
接下來我們將使用下圖作爲圖的創建,提前將信息輸入到 graph.conf
中,再讓程序進行讀取。
- graph.conf
7
10
A B C D E F G
0 1 5
0 2 2
1 3 1
2 3 6
1 4 6
3 4 1
3 5 2
2 5 8
4 6 7
5 6 3
# 第一行 頂點個數
# 第二行 邊個數
# 第三行 頂點的元素
# 往下 邊連接的兩個頂點 以及 權
一.Dijkstra算法求解單源最短路徑
在上一篇中,我們已經瞭解到,如果是非加權圖(或者是權值均爲1的加權圖), 直接使用BFS
方法就可以算出單源的最短距離。
但如果是權值不相等的加權圖,那麼做一點小小的改進,也就成了Dijkstra算法。
Djikstra思想:
使用BFS,比較並記錄單源點到其他點的最小路徑。
- 程序:
#include <iostream>
#include <vector>
#include <fstream>
#include <sstream>
#include <stack>
#include <queue>
using namespace std;
/*********************************圖*********************************************/
/*
因爲使用Dijkstra算法中只可能是單節點向前推薦,所以採用有向圖的方式(實際上無向圖也可以,運算時看作有向圖)
採用鄰接表形式創建圖(有向圖,連通圖, 加權圖):
vertex : 頂點
EdgeNode: 邊節點
*/
using ElemType = char;
struct EdgeNode
{
int index; // 用於尋找頂點位置的索引
int weight; //權
struct EdgeNode *next;
EdgeNode(int index, int weight) : index(index), weight(weight), next(NULL) {}
};
struct Vertex
{
int index;
ElemType val;
struct EdgeNode *adjNode;
Vertex(int index, ElemType val) : index(index), val(val), adjNode(NULL) {}
};
/*********************************類申明*********************************************/
class Graph
{
vector<Vertex *> _vertexs; //頂點
int _vertexNum; //頂點數量
int _edgeNum; //邊的數量,注意是無向圖
vector<int> visited; //判斷定點是否訪問過
public:
Graph();
int dijkstra_minDistance(int beg, int end); //Dijkstra算法 求解加權圖的最短距離
void printAdjList(); //測試鄰接表是否正確組合
Vertex *getRoot() const { return _vertexs[0]; }
void resetVisited()
{
for (int i = 0; i < _vertexNum; i++)
visited[i] = 0;
}
private:
void createGraph();
void insertEdgeNode(Vertex *v, EdgeNode *node); //插入邊節點,用頭插法
};
/*********************************類定義*********************************************/
Graph::Graph()
{
createGraph();
visited.resize(_vertexNum);
}
void Graph::createGraph()
{
std::ifstream ifs("graph.conf", ios::in);
string str;
//第一行,讀取圖頂點的個數
{
getline(ifs, str);
stringstream ss(str);
ss >> _vertexNum;
}
//第二行,讀取邊的數量
{
getline(ifs, str);
stringstream ss(str);
ss >> _edgeNum;
}
//第三行,讀取頂點元素
{
getline(ifs, str);
stringstream ss(str);
ElemType val;
for (int i = 0; i < _vertexNum; i++)
{
ss >> val;
Vertex *newNode = new Vertex(i, val);
_vertexs.push_back(newNode);
}
}
//接下來,進行邊的連接
{
for (int i = 0; i < _edgeNum; i++)
{
getline(ifs, str);
stringstream ss(str);
int index1, index2, weight; //用邊連接的兩個頂點的編號
ss >> index1 >> index2 >> weight;
EdgeNode *newNode = new EdgeNode(index2, weight);
insertEdgeNode(_vertexs[index1], newNode);
}
}
ifs.close();
}
void Graph::insertEdgeNode(Vertex *v, EdgeNode *node)
{
node->next = v->adjNode;
v->adjNode = node;
}
void Graph::printAdjList()
{
for (int i = 0; i < _vertexNum; i++)
{
cout << "Vertex " << _vertexs[i]->val << " -> ";
Vertex *v = _vertexs[i];
EdgeNode *n = v->adjNode;
while (n)
{
cout << _vertexs[n->index]->val << "(weight:" << n->weight << ")"
<< " -> ";
n = n->next;
}
cout << "NULL" << endl;
}
}
//Dijkstra算法,本質上還是使用BFS遍歷,只是在每一層判斷路徑和
int Graph::dijkstra_minDistance(int beg, int end)
{
vector<int> path;
path.resize(_vertexNum);
vector<int> distance;
for (int i = 0; i < _vertexNum; i++)
distance.push_back(65536);
distance[beg] = 0;
std::queue<int> que;
que.push(beg);
while (!que.empty())
{
Vertex *v = _vertexs[que.front()];
que.pop();
EdgeNode *node = v->adjNode;
while (node)
{
que.push(node->index);
if (distance[node->index] > distance[v->index] + node->weight)
{
distance[node->index] = distance[v->index] + node->weight;
path[node->index] = v->index;
}
node = node->next;
}
}
//打印path操作
vector<ElemType> printPath;
int i = end;
while (i != beg)
{
printPath.push_back(_vertexs[i]->val);
i = path[i];
}
printPath.push_back(_vertexs[beg]->val);
for (auto it = printPath.rbegin(); it != printPath.rend(); it++)
cout << *it << "->";
cout << endl;
return distance[end];
}
/*********************************測試函數*********************************************/
//打印
void test0()
{
Graph graph;
graph.printAdjList();
int beg = 0;
int end = 6;
cout << graph.dijkstra_minDistance(beg, end) << endl;
}
int main()
{
test0();
return 0;
}
二.Floyd算法求解多源最短路徑
Dijkstra算法只能求解一個點到其他店的最短路徑,那麼如果要求任意兩點之間的最短路徑,雖然也可以將Dijkstra算法再執行N次,但是Floyd算法卻更簡潔有效。
Floyd算法核心
:必須使用鄰接矩陣,依次以不同的頂點爲中繼頂點,在能夠作爲中繼節點到達的情況下,依次計算出最小路徑。
Floy算法也是屬於動態規劃
中的重要算法。
_matrix[i][j] = std::min(_matrix[i][j], _matrix[i][k]+_matrix[k][j]);
- 程序
#include <iostream>
#include <vector>
#include <fstream>
#include <sstream>
#include <stack>
#include <queue>
#include <climits>
#include <iomanip>
#include <algorithm>
using namespace std;
/*********************************圖*********************************************/
/*
實現Floyd算法必須使用鄰接矩陣的存儲方式,所以採用鄰接矩陣形式創建圖(無向圖,連通圖, 加權圖):
_matrix 矩陣
*/
using ElemType = char;
/*********************************類申明*********************************************/
class Graph
{
vector<vector<int>> _matrix; //鄰接矩陣
vector<ElemType> _vals; //存放頂點元素
int _vertexNum; //頂點數量
int _edgeNum; //邊的數量,注意是無向圖
vector<int> visited; //判斷定點是否訪問過
public:
Graph();
void Floyd_minDistance(); //弗洛伊德算法 求解多源最短路徑
void printAdjMatrix(); //測試鄰接矩陣是否正確組合
void resetVisited()
{
for (int i = 0; i < _vertexNum; i++)
visited[i] = 0;
}
private:
void createGraph();
};
/*********************************類定義*********************************************/
Graph::Graph()
{
createGraph();
visited.resize(_vertexNum);
}
void Graph::createGraph()
{
std::ifstream ifs("graph.conf", ios::in);
string str;
//第一行,讀取圖頂點的個數
{
getline(ifs, str);
stringstream ss(str);
ss >> _vertexNum;
_matrix.resize(_vertexNum);
for (int i = 0; i < _vertexNum; i++)
_matrix[i].resize(_vertexNum);
for (int i = 0; i < _vertexNum; i++)
for (int j = 0; j < _vertexNum; j++)
if (i == j)
_matrix[i][j] = 0;
else
_matrix[i][j] = INT_MAX;
}
//第二行,讀取邊的數量
{
getline(ifs, str);
stringstream ss(str);
ss >> _edgeNum;
}
//第三行,讀取頂點元素
{
getline(ifs, str);
stringstream ss(str);
ElemType val;
for (int i = 0; i < _vertexNum; i++)
{
ss >> val;
_vals.push_back(val);
}
}
//接下來,進行邊的連接
{
for (int k = 0; k < _edgeNum; k++)
{
getline(ifs, str);
stringstream ss(str);
int i, j, weight; //用邊連接的兩個頂點的編號
ss >> i >> j >> weight;
_matrix[i][j] = weight;
_matrix[j][i] = weight;
}
}
ifs.close();
}
void Graph::printAdjMatrix()
{
cout << "-----------------------------------" << endl;
for (int i = 0; i < _vertexNum; i++)
{
for (int j = 0; j < _vertexNum; j++)
{
if (INT_MAX == _matrix[i][j])
cout << " ∞ ";
else
cout << " " << _matrix[i][j] << " ";
}
cout << endl;
}
cout << "-----------------------------------" << endl;
}
void Graph::Floyd_minDistance()
{
for (int k = 0; k < _vertexNum; k++)
for (int i = 0; i < _vertexNum; i++)
for (int j = 0; j < _vertexNum; j++)
if (0==_matrix[i][j] || INT_MAX==_matrix[i][k] || INT_MAX==_matrix[k][j])
continue;
else
_matrix[i][j] = std::min(_matrix[i][j], _matrix[i][k]+_matrix[k][j]);
}
/*********************************測試函數*********************************************/
//打印
void test0()
{
Graph graph;
graph.printAdjMatrix();
graph.Floyd_minDistance();
cout << "After Floyd algorithm:" << endl;
graph.printAdjMatrix();
}
int main()
{
test0();
return 0;
}