图论————迪杰斯特拉和弗洛伊德(最短路径问题)

一、迪杰斯特拉算法(单源最短路径)路径不可为负数

回来忘记了 可以看这个视频 看思路 链接-迪杰斯特拉算法思路视频

  • 借用贪心算法的思想:由局部的最优路径找到整体的最优路径。

前言:

  1. 定义一个dis[i] 数组:记录源点到其他顶点的最短距离(到达不了的记为无穷大)。
  2. 定义一个**vit[i] **数组:记录已经便标记过的顶点。(标记过为1 没有标记过为 0。
  3. 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;
}

二 弗洛伊德算法(多源最短路径)路径可为负数

步骤思路

  1. 用邻接矩阵表示出来相应的图。
  2. 逐步在原图路径中添加中间点,形成一个新的路径
  3. 比较新路径和原路径的大小,取小的路径值 更新邻接矩阵
  4. 直到所有的点都尝试完毕,算法结束。
    简易步骤图

参考代码

核心代码
        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;
}

三、佛洛依德和迪杰斯特拉比较认识

  1. 迪杰斯特拉只能求解单一 一个点到另一个特定点的最短路径,权值还不能是负值,时间复杂度是O(n^2)用堆来优化就是O(n)。
  2. 佛洛依德求解的任意两个顶点的最短路径,权值可以是负数,但是在一个回路里面的权值不能是负值.时间复杂度是O(n^3)。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值