6.5最短路径

最短路径

对于网图来说,最短路径是指两顶点之间经过的边上的权值之和最少的路径,并且我们称路径上的第一个顶点是源点最后一个顶点是终点

下面主要介绍两种解决最短路径的算法:

  • 迪杰斯特拉(Dijkstra)算法:主要从某个源点到其余各顶点的最短路径问题时间复杂度为O(n^2)
  • 弗洛伊德(Floyd)算法:主要用来解决从所有顶点到所有顶点间的最短路径问题时间复杂度为O(n^3)

1.迪杰斯特拉(Dijkstra)算法

下面从一个例子开始:

例:用迪杰斯特拉算法对下列网图求顶点1到各顶点的最短路径。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ydboXEUJ-1637584644961)(C:\Users\ThinkStation K\AppData\Roaming\Typora\typora-user-images\image-20211122192723118.png)]

其邻接矩阵为:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nUqmfEs4-1637584644962)(C:\Users\ThinkStation K\AppData\Roaming\Typora\typora-user-images\image-20211122192812166.png)]

数组Dis用来表示顶点1到各顶点的最短距离Fin数组用来记录已求出最短路径的顶点,如果已求出最短路径则为1,否则为0。

算法过程如下:

  • 顶点1到自身的距离为0,因此给Fin[0]为1表示顶1已经求出到自身的最短距离。它到各点的最短距离暂时放在Dis数组中。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OlgwWl2X-1637584644964)(C:\Users\ThinkStation K\AppData\Roaming\Typora\typora-user-images\image-20211122193507640.png)]

  • 顶点1所能到达顶点为顶点2和顶点3,权值分别为1和12。此时知道顶点1到顶点2的最短距离为1,将顶点2对应的Fin[1]设置为1表示已经求出顶点1到顶点2的最短距离。顶点2到顶点3的距离为9,9+1<12,因此用10代替Dis[2]的值,顶点2到顶点4的值为3,3+1 = 4更新Fin[3]的值。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hy3MjCVV-1637584644965)(C:\Users\ThinkStation K\AppData\Roaming\Typora\typora-user-images\image-20211122195132519.png)]

  • 此时,在还没求出最短路径的顶点中,顶点1能到达的顶点距离最小值为4,对应顶点为4,将顶点4对应的Fin[3]设置为1表示已经求出顶点1到顶点4之间的最短距离。顶点4到顶点3的距离为4,4+4<10,因此顶点1到顶点4的最短距离为8;顶点1到顶点5的最短距离为4+13=17;顶点1到顶点6的最短距离为4+15=19。更新Dis数组。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jSbR4MU5-1637584644965)(C:\Users\ThinkStation K\AppData\Roaming\Typora\typora-user-images\image-20211122195842166.png)]

  • 接下来同上步骤,在还没求出最短路径的定点中,顶点1能到达的顶点距离最小值为4,对应顶点为3,将顶点3对应的Fin[2]设置为1表示已经求出顶点1到顶点3之间的最短距离。顶点3到顶点5的距离为5,5+8<17,因此顶点1到顶点5的最短距离为13。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mFhDQszb-1637584644966)(C:\Users\ThinkStation K\AppData\Roaming\Typora\typora-user-images\image-20211122200105233.png)]

  • 顶点5最顶点1能到达的距离最小的顶点,将Fin[4]设置为1。顶点5到顶点6的距离为4,13+4 < 19,因此顶点1到顶点6的最短距离为17。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d4KHh2AR-1637584644967)(C:\Users\ThinkStation K\AppData\Roaming\Typora\typora-user-images\image-20211122200230268.png)]

    上述例子的代码如下:

    const int m = INT16_MAX;
    struct Graph {
    	int vexs[6] = {0, 1, 2, 3, 4, 5};
    	int arc[6][6] =
    	{
    		{0, 1, 12, m, m, m},
    		{m, 0, 9, 3, m, m},
    		{m, m, 0, m, 5, m},
    		{m, m, 4, 0, 13, 15},
    		{m, m, m, m, 0, 4},
    		{m, m, m, m, m, 0},
    	};
    	int numVex = 6, numEdg = 9;
    };
    
    void ShortestPath_Dijkstra(Graph g, int v0, int D[]) {
    	int min, k = 0;
    	int final[6];
    	for (int i = 0; i < g.numVex; ++i) {
    		final[i] = 0;
    		D[i] = g.arc[v0][i];
    	}
    
    	D[v0] = 0;
    	final[v0] = 1;
    
    	for (int i = 1; i < g.numVex; ++i) {
    		min = INT16_MAX;
    		for (int j = 0; j < g.numVex; ++j) {
    			if (!final[j] && D[j] < min) {
    				k = j;
    				min = D[j];
    			}
    		}
    		final[k] = 1;
    		for (int j = 0; j < g.numVex; ++j) {
    			if (!final[j] && (min + g.arc[k][j] < D[j])) {
    				D[j] = min + g.arc[k][j];
    			}
    		}
    	}
    }
    
    int main() {
    	Graph g;
    	int d[6] = {};
    	ShortestPath_Dijkstra(g, 0, d);
    	for (int i = 0; i < 6; ++i) {
    		cout << d[i] << " ";
    	}
    }
    

从上述过程可以总结出迪杰斯特拉算法:首先找出当前点到所有能到达的点之间最短的距离,然后在已经访问过的点中遍历一遍,看看有没有更近的,如果有更近的就更新距离。然后重复上述过程就能找到最短路径。

2.弗洛伊德算法

上述例子的代码为:

const int m = INT16_MAX;
struct Graph {
	int vexs[6] = {0, 1, 2, 3, 4, 5};
	int arc[6][6] =
	{
		{0, 1, 12, m, m, m},
		{m, 0, 9, 3, m, m},
		{m, m, 0, m, 5, m},
		{m, m, 4, 0, 13, 15},
		{m, m, m, m, 0, 4},
		{m, m, m, m, m, 0},
	};
	int numVex = 6, numEdg = 9;
};

void ShortestPath_Floyd(Graph g, int P[6][6], int D[6][6]) {
	for (int i = 0; i < g.numVex; ++i) {
		for (int j = 0; j < g.numVex; ++j) {
			D[i][j] = g.arc[i][j];
			P[i][j] = j;
		}
	}
	for (int k = 0; k < g.numVex; ++k) {
		for (int i = 0; i < g.numVex; ++i) {
			for (int j = 0; j < g.numVex; ++j) {
				if (D[i][j] > D[i][k] + D[k][j]) {
					D[i][j] = D[i][k] + D[k][j];
					P[i][j] = P[i][k];
				}
			}
		}
	}
}

int main() {
	Graph g;
	int d[6][6] = {};
	int p[6][6] = {};
	ShortestPath_Floyd(g, p, d);
	for (int i = 0; i < 6; ++i) {
		for (int j = 0; j < 6; ++j) {
			cout << d[i][j] << "\t";
		}
		cout << endl;
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值