C++:图的最短路径问题

一、简介

        在非网图中,最短路径是指两顶点之间经历的边数最少的路径。在网图中,最短路径是指两顶点之间经历的边上权值之和最少的路径。

         路径上的第一个顶点称为源点,最后一个顶点称为终点

        最短路径问题是图的一个比较典型的应用问题。例如,给定某公路网的n个城市以及这些城市之间相通公路的距离,能否找到城市A到城市B之间一条距离最近的通路呢?如果将城市用顶点表示城市间的公路用边表示,公路的长度作为边的权值,这个问题就归结为在网图中求顶点A和顶点B的最短路径。

        最短路径问题常见的两种算法是Dijkstra算法Floyd算法

         

二、Dijkstra算法 

         Dijkstra算法是一种用于求解单源最短路径的经典算法。它可以在带权重的、有向或无向的图中找出从起始点所有其他节点的最短路径。该算法要求边的权重为非负数,因此不能处理带有负权重的图。

        其基本思想是通过贪心策略,每次选择未处理节点中距离起始点最近的节点,逐步找到最短路径。

        图采用邻接矩阵存储。

        Dijkstra算法主要包括以下步骤:

  1. 初始化

    • 创建一个数组 dist,用来记录从起点到各个节点的最短距离。初始时,起点到自身的距离为0,其它节点的距离设为正无穷大。
    • 创建一个bool数组 visited,用来记录节点是否已经被处理过。初始时,所有节点都未被处理过。
  2. 循环查找

    • 每次从未被处理过的节点中选出距离起点最近的节点 u,并标记为已处理。
    • 遍历所有与 u 相邻的节点 v,如果通过 u 到达 v 的路径更短,则更新 dist[v],并将 u 作为 v 的前驱节点。
  3. 重复

    • 重复以上步骤,直到所有节点都被处理或找到的最短路径已经不能再更新。
  4. 终止

    • 所有节点被标记为已处理,或者没有可以继续处理的节点时,算法终止。此时,数组 dist 中存放的即是起点到各节点的最短距离。

 

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


//功能:通过Kijkstra算法求最短路径
//参数:输入起点src、顶点数vertex_num、邻接矩阵edge
vector<int> kijkstra(int src, int vertexNum, vector<vector<int>> edge)
{
	vector<int> dist(vertexNum, INT_MAX);
	//存放起点到各顶点的最近距离,初始为最大值INT_MAX
	vector<bool> visited(vertexNum, false);
	//存放各顶点的访问状态,初始为未访问false
	
	
	for (int i = 0; i < vertexNum; i++)       //更新起点到各顶点的距离
	{
		dist[i] = edge[src][i];
	}
	dist[src] = 0;                            //起点到起点的距离为0
	visited[src] = true;                      //将起点标记为已访问

	//遍历所有节点,更新起点到各节点的最小距离(更新dist[]数组),最多执行(顶点数-1)次即可,实际上(顶点数-2)应该够了
	for (int j = 0; j < vertexNum-1; j++)
	{
		int min(INT_MAX), u(-1);              //存储未访问节点中距离起点最近的点的值和下标
		for (int k = 0; k < vertexNum; k++)   //遍历所有节点
		{
			if (!visited[k] && dist[k] < min) //若该节点未被访问且起点到该点的距离较近
			{
				min = dist[k];
				u = k;                        //u为未访问的点中距离起点最近的点
			}
		}
		if (u == -1)                          //若没有未访问的点或者没有连通的点
		{
			return dist;                      //返回结果,包含起点到各点的最短距离
		}
		visited[u] = true;                   //将最近的这个顶点标记为已访问

		for (int n = 0; n < vertexNum; n++)  //更新起点到各顶点的距离,使起点到各顶点的距离最近
		{
			if (dist[n] > dist[u] + edge[u][n] && !visited[n]) 
			{                                //若从起点到u再到n的距离比直接从起点到n的距离还小
				dist[n] = dist[u] + edge[u][n];
			}
		}
	}
	return dist;                             //返回结果,包含起点到各点的最短距离
}

int main()
{
	//设置起点为0,顶点数为5
	int src(0),dest(4), vertex_num(5);
	//图的邻接矩阵,INT_MAX表示两点之间无边,0表示同一个点之间的距离
	vector<vector<int>> edge = {{0      , 10     , 3      , INT_MAX, INT_MAX},
								{INT_MAX, 0      , 1      , 2      , INT_MAX},
								{INT_MAX, 4      , 0      , 8      , 2      },
								{INT_MAX, INT_MAX, INT_MAX, 0      , 7      },
								{INT_MAX, INT_MAX, INT_MAX, 9      , 0      }};
	//存放起点到各点的最短距离
	vector<int> dist;
	//调用Kijkstra算法求最短路径
	dist = kijkstra(src, vertex_num, edge);
	//输入起点到终点的最短距离
	cout << dist[dest];

	return 0;
}

 

三、Floyd算法

        Floyd算法是一种用于求解图中任意两个顶点之间最短路径的算法。它适用于带权有向图和无向图,能够处理负权边(但不支持负权回路)。

        Floyd算法的核心思想是利用动态规划的原则,通过对图中每对顶点之间的路径进行更新,逐步找到所有顶点对之间的最短路径。算法使用一个二维数组 dist,其中 dist[i][j] 表示从顶点 i 到顶点 j 的最短路径的权重。  

        同Dijkstra算法,Floyd算法适用于图的邻接矩阵存储结构。

        以下是Floyd算法的具体步骤:

  1. 输入:图的顶点数vertexNum 和边的列表(每条边包含起始顶点、结束顶点及权重)。
  2. 初始化距离矩阵 dist
    • 对于每个顶点 i,设置 dist[i][i] = 0
    • 对于每条边 (u, v, w),设置 dist[u][v] = w
    • 其余的 dist[i][j] 设置为INT_MAX。
  3. 动态规划更新
    • 使用三重循环遍历所有顶点组合 (i, j, k),进行路径更新。
  4. 输出:最短路径矩阵 dist

 

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


//输入参数:起点src、终点dest、顶点数vertexNum,邻接矩阵edge
void Floyd(int src,int dest,int vertexNum, vector<vector<int>> edge)
{
	vector<vector<int>> dist(vertexNum, vector<int>(vertexNum, INT_MAX));
	//最短距离矩阵,存储两点之间的最短距离
	for (int i = 0; i < vertexNum; i++)
	{
		for (int j = 0; j < vertexNum; j++)
		{
			if (i == j)
			{
				dist[i][j] = 0;          //自己到自己的距离为0
			}
			else if (edge[i][j] != 0)
			{
				dist[i][j] = edge[i][j]; //设置边的权重
			}
		}
	}

	//Floyd算法
	for (int k = 0; k < vertexNum; k++)
	{
		for (int i = 0; i < vertexNum; i++)
		{
			for (int j = 0; j < vertexNum; j++)
			{
				if (dist[i][k] != INT_MAX && dist[k][j] != INT_MAX)//判断i到k,k到j是否通
				{
					dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
					//看是i直接到j更近,还是i先经过k再到j更近
				}
			}
		}
	}

	//输出结果
	cout << dist[src][dest] << endl;
}

int main()
{
	// 顶点数量
	int vertex_num = 4; 
	// 图的邻接矩阵
	vector<vector<int>> edge = {{0, 5, 0, 10},
								{0, 0, 3, 0 },
								{0, 0, 0, 1 },
								{0, 0, 0, 0 }};

	//调用Floyd算法求最短路径
	Floyd(0, 3, vertex_num, edge);

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值