一、迪杰斯特拉算法(单源最短路径)路径不可为负数
回来忘记了 可以看这个视频 看思路 链接-迪杰斯特拉算法思路视频
- 借用贪心算法的思想:由局部的最优路径找到整体的最优路径。
前言:
- 定义一个dis[i] 数组:记录源点到其他顶点的最短距离(到达不了的记为无穷大)。
- 定义一个**vit[i] **数组:记录已经便标记过的顶点。(标记过为1 没有标记过为 0。
- min 记录源点到其他顶点的距离中最小的距离,
- 什么叫单源最短路径呢?
一个顶点到其余顶点的最短路径,记录的是一个顶点, 所以叫单源。
思路过程
1.首先在顶点集里面选择一个顶点作为源点,由这个顶点出发 更新到能够到达顶点的距离(更新dis[]数组的值)
2. 选取能够到达的顶点中距离最小的顶点一个最为下一个的起点,并把该顶点标记为1(更新vit[]数组)
3. 重复步骤二 直到(除最后一个)顶点都被标记过。
经过上面的步骤 在数组di数组的值就是到达各个顶点的最短路径长度。
参考代码
#include <iostream>
#include <cstring>
#include <limits.h>
using namespace std;
// 定义相关参数
const int MAX_N = 500; // 最大顶点数
int graph[MAX_N + 1][MAX_N + 1]; // 以二维数组graph表示图,注意点的编号是从1开始的
int dis[MAX_N + 1]; // 从起点s到其他点的距离数组dis
bool mark[MAX_N + 1]; // 记录某点是否已被访问的数组
int n, E, s, t; // 顶点数n、边数E、起点s以及终点t
// 主函数
int main(int argc, const char * argv[]) {
// 共T组测试用例
int T;
cin >> T;
while((T--) > 0) {
// 初始化,注意memset(..., 127, ...)是将数组中的所有元素全为2139062143,即无穷大
memset(graph, 127, sizeof(graph));
memset(dis, 127, sizeof(dis));
memset(mark, false, sizeof(mark));
// 输入顶点数n、边数E、起点s以及终点t
cin >> n >> E >> s >> t;
// 顶点u到顶点v之间无向边长度w
// 因为可能有重边,所以需要取最小者
// 虽然是无向图,但是我们改成有向图来处理
for(int ei = 0; ei < E; ++ei) {
int u, v, w;
cin >> u >> v >> w;
graph[u][v] = min(graph[u][v], w);
graph[v][u] = min(graph[v][u], w);
}
// 开始Dijkstra算法
// 起点到起点的距离应该为0
dis[s] = 0;
// 外层大循环保证所有点都会被访问到
// 在输入的时候并没有更新dis数组,因为很明显第一个被访问的就是起点s(距离dis[s]为0)
for(int ni = 1; ni <= n; ++ni) {
// 记录当前访问的点的变量
int currentDis = INT_MAX;
int currentVex;
// 在未被访问过的点中寻找当前需要访问的点
// 也就是未被访问过的、和起点距离最短的点
for(int cvi = 1; cvi <= n; ++cvi) {
if((!mark[cvi]) && (dis[cvi] < currentDis)) {
currentDis = dis[cvi];
currentVex = cvi;
}
}
// 将该点设置成已访问状态
mark[currentVex] = true;
// 更新所有和当前点连接的点的距离
/*
问:这边为什么不用考虑更新已经访问过的点的距离呢?
答:因为已访问的点到起点s的距离一定小于当前点到起点s的距离,
所以从起点s到当前点再到已访问的点的距离一定大于从起点s到已访问的点的距离
*/
for(int i = 1; i <= n; ++i) {
// 如果点i未被访问、且从起点s到当前点currentVex再到点i的距离小于现有的从起点s到点i的距离的话,则更新
if((!mark[i]) && (dis[currentVex] + graph[currentVex][i] < dis[i])) {
dis[i] = dis[currentVex] + graph[currentVex][i];
}
}
}
// Dijkstra算法结束,从代码中的两层for循环可以很明显看出时间复杂度为O(n^2)
// 输出结果(如果从起点s无法到达终点t则输出-1)
if(dis[t] == INT_MAX) {
cout << -1 << endl;
}
else {
cout << dis[t] << endl;
}
}
return 0;
}
二 弗洛伊德算法(多源最短路径)路径可为负数
- 链接- 推荐学习视频链接
- 什么叫多源呢?
解决每两个任意顶点间的最短路径。
步骤思路
- 用邻接矩阵表示出来相应的图。
- 逐步在原图路径中添加中间点,形成一个新的路径
- 比较新路径和原路径的大小,取小的路径值 更新邻接矩阵
- 直到所有的点都尝试完毕,算法结束。
参考代码
核心代码
for (int k = 0; k < size; k++)
{
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
if (graph[i][j] > graph[i][k] + graph[k][j])
{
graph[i][j] = graph[i][k] + graph[k][j];
}
}
}
}
全部代码
#include<iostream>
#include<vector>
#include<iomanip>
using namespace std;
void InputGraph(vector<vector<int>> &graph) //输入权值,否则为无穷大
{
int count = 0;
cin >> count;
int size=graph.size();
for (int i = 0; i < size; i++)
{
graph[i][i] = 0;
}
for (int i = 0; i < count; i++)
{
int start = -1, end = -1, weight = 0;
cin >> start >> end >> weight;
graph[start][end] = weight;
}
}
void Floyd(vector<vector<int>> &graph) //使用佛洛依德算法求最短路径长度
{
int size = graph.size();
for (int k = 0; k < size; k++)
{
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
if (graph[i][j] > graph[i][k] + graph[k][j])
{
graph[i][j] = graph[i][k] + graph[k][j];
}
}
}
}
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
cout <<setw(6)<< graph[i][j] << " ";
}
cout << endl;
}
}
int main()
{
vector<vector<int>> graph = vector<vector<int>>(5, vector<int>(5, 999999));
InputGraph(graph);
Floyd(graph);
system("pause");
return 0;
}
三、佛洛依德和迪杰斯特拉比较认识
- 迪杰斯特拉只能求解单一 一个点到另一个特定点的最短路径,权值还不能是负值,时间复杂度是O(n^2)用堆来优化就是O(n)。
- 佛洛依德求解的任意两个顶点的最短路径,权值可以是负数,但是在一个回路里面的权值不能是负值.时间复杂度是O(n^3)。