leetcode(7):图论Dijkstra||


图论问题一般分为:

  • 单源最短路径问题:从某固定源点出发,求其到所有其他顶点的最短路径
    • (有向)无权图
    • (有向)有权图
  • 多源最短路径问题:求任意两顶点间的最短路径

1.迪杰斯特拉Dijkstra

1.1原理详解

有权图的单源最短路径算法——dijkstra;
不能处理带负边权的情况,用邻接矩阵邻接表存图
——————————————————————————
所以 最短路径无向图正边权——Dijkstra算法
———————————————————————————
Dijkstra进行进一步的堆优化以后时间复杂度成为O(nlogn),
Floyd的O(n^3)
Dijkstra,以及常用的还有Bellman-Ford,SPFA等均是在求单源最短路径的问题中有着较为理想的时间复杂度(<=O(n^2)),
但若是求图中任意两点间的距离,尤其是在图较为稠密时,Floyd的O(n^3)也是不输于其他的。
在这里插入图片描述————————————————————————————————————
在这里插入图片描述

实现思路——个人理解:
函数原型 Dijkstra(int s)

  1. visited数组,distance数组,pre前驱点数据||初始化,visited全部未访问,distance数组全部最大;distance表示各个其他点距离s的距离;pre表示当前点前一个点,初始化为-1(和dijkstra原理有关,原理移步B站

  2. distance初始化,dis[s] = 0;

  3. 根据邻接矩阵和邻接表;遍历所有点,对与s相连通的点的dis[index]进行初始化,dis[index] = map[s][index] (邻接矩阵);其中图中与s不连通的点,其dis值仍为inf;同时更新上述连通的前驱点

     					pre[index] = s;
    
  4. 上面是初始化操作,下面是建dijikstra表操作

  5. 遍历所有点,for(int index = 0;index<N;index++) 。找出未访问过的距离起始点s最近的一个点;(就拿上面的图来说,倘若起始点s=A,此时遍历所有点,会找到D点,因为D点距离A最近)。保存该点的dis值和索引值,将该点的dis报错为distemp,索引值保存为indextemp;最重要的要标记该点已经访问过了;visited[index] = true; ————————访问数组visit第五步用到

  6. 再次遍历所有点;目的是为了对已建好的表进行优化;

  7. 遍历所有点,for(int index = 0;index<N;index++) 。在未访问过的店中,检查dis[index]与dis[indextemp]+ map[index] [indextemp] 的值的大小。如果后者更小的话,就需要对图进行优化。更新距离值和前驱点:————————访问数组visit第六步也用到

dis[index] = dis[indextemp]+ map[index] [indextemp];
pre[index] = indextemp;  
  1. 针对结果输出的话

    - s点到i点的距离:直接输出dis[i]的值就行
    - s点到i点的路径:
    
//目标点的前驱点为 p[i]
// p[i]点的前驱点 p[p[i]] ……
void prin(int i,int s)
{
//倒序输出
	cout<<p[i]<<" "
	if(p[i] != s)
	    prin(p[i],s);
}

1.2例题应用

以上面那张图为例ABCDE

#include <iostream>
#include <string>
#include <sstream>
#include <cstring>

using namespace std;


#define N 5
#define INF 0x3f3f3f

int dis[N];
bool visit[N];
int pre[N];

// void Dijkstra(int s) //建表过程 ,建立图中所有点与s的最短路径
int Dijkstra(int s,int e,int map[N][N])//s->e的最短路径
{
	memset(visit, false, sizeof(visit));
	memset(dis, INF, sizeof(dis));
	memset(pre, -1, sizeof(pre));

	dis[s] = 0;

	for (int i = 0; i < N; i++)
	{
		dis[i] = map[s][i];
		pre[i] = s;
	}
	//

	//建表操作
	for (int i = 0; i < N; i++)
	{
		int distemp = INF;
		int indextemp;
		//找一个最近点 遍历所有点
		for (int j = 0; j < N; j++)
		{
			if ( !visit[j] && dis[j] < distemp)
			{
				distemp = dis[j];
				
				indextemp = j;
			}
		}
		visit[indextemp] = true; //标记访问过

		//	第二次遍历 目的是优化
		for (int j = 0; j < N; j++) 
		{
			if (!visit[j] && dis[j] > map[indextemp][j] + distemp) //言外一直 a->d的直接距离 大于 a->index->d
			{
				dis[j] = map[indextemp][j] + distemp;
				pre[j] = indextemp;//前驱点更新 
			}
		}
	}
	//所有遍历完,表就建好了,有用的主要是前驱表pre[]和距离表dis[]
	return dis[e];
}

void prin(int i,int s)
{
	cout << pre[i] << "<-";
	if(pre[i]!=s)
		prin(pre[i],s);
}
int main()
{
	int map[5][5] =
		{
			{0, 6, INF, 1, INF}, //不直接相连的点距离就是无穷大
			{6, 0, 5, 2, 2},
			{INF, 5, 0, 5, INF},
			{1, 2, INF, 0, 1},
			{INF, 2, 5, 1, 0},
		};

	int res = Dijkstra(0,2,map); 

	cout << res << endl;

	cout << "2";
	prin(2,0);
	return 0;
}


猜你喜欢

转载自blog.csdn.net/CoomCon/article/details/107677421
今日推荐