手撸贪心算法之最短路径

算法介绍

这个算法我们生活中会经常使用到,比如我们到了一个新的城市。经常会使用高德地图导航来找到距离目的地的最短路径,地图会给出我们具体的行走路线,大约需要花费的时间。一般会给出最短时间和最短路径两种方案,起始算法是一样的。只需要将路径的权值换为平均时间就可以。

这里我们讨论的是需要到多个目的地,求出分别到每个目的地的最短距离。比如我们今天想去“颐和园”,明天想去“长城”。这是一个求单元最短路径的问题。给定有向带权图 G = ( V , E ) G = (V,E) G=(V,E),其中每条边的权是非负实数。此外,给定V中的一个顶点,称为源点。这里的路径长度指路上各边的权之和。
强烈推荐小白下载这个APP,快速搞懂算法运行流程。
算法动态图解:链接:https://pan.baidu.com/s/1mX3s7VjLTKLr7MZhAQO-6Q
提取码:cv5y

算法流程

  • 1 数据结构,设置地图的带权邻接矩阵为 m a p [ ] [ ] map[][] map[][],即如果从源点 u u u到顶点 i i i有边,令 m a p [ u ] [ i ] map[u][i] map[u][i]=两个点之间的距离,否则将 m a p [ u ] [ i ] map[u][i] map[u][i]= ∞ \infty ;采用一维数组 d i s t [ ] i dist[]i dist[]i记录从顶点 i i i到源点的距离;采用一维数组 p [ ] p[] p[]来记录最短距离 i i i顶点的前驱。
  • 2 初始化。令集合 S = { u } S=\{u\} S={ u},对于集合中 V − S V-S VS中的所有顶点 x x x,初始化 d i s t [ i ] = m a p [ u ] [ i ] dist[i]=map[u][i] dist[i]=map[u][i],初始化前驱数组 p p p,如果顶点 i i i到源点 u u u有边相连,初始化 p [ i ] = u p[i]=u p[i]=u,否则 p [ i ] = − 1 p[i]=-1 p[i]=1
  • 3 找最小。在集合 V − S V-S VS中依照贪心策略来U型你找是的 d i s t [ i ] dist[i] dist[i]最小的顶点 t t t,即 d i s t [ i ] dist[i] dist[i] = m i n ( d i s t [ j ] ) min(dist[j]) min(dist[j]) d i s t [ j ] dist[j] dist[j]属于 V − S V-S VS集合,则顶点 t t t就是集合 V − S V-S VS中距离源点 u u u最近的顶点。
  • 4 加入 S S S集合。将顶点 t t t加入集合 S S S中,同事更新 V − S V-S VS
  • 5.判断是否结束。如果 V − S V-S VS集合为空,则算法结束,否则进行第6步
  • 6 借东风。在步骤3中已经找到了源点到 t t t的最短路径,那么对于集合 V − S V-S VS中所有与顶点 t t t相邻的顶点 j j j,都可以借助 t t t走捷径。前提是捷径的距离更短,即 d i s t [ j ] > d i s t [ t ] + m a p [ t ] [ j ] dist[j]>dist[t]+map[t][j] dist[j]>dist[t]+map[t][j],则 d i s t [ j ] = d i s t [ t ] + m a p [ t ] [ j ] dist[j]=dist[t]+map[t][j] dist[j]=dist[t]+map[t][j],记录顶点 j j j的钱去为 t t t。有 p [ j ] = t p[j]=t p[j]=t。然后继续步骤3。

源代码

#include<iostream>
#include<windows.h>
#include<stack>


using namespace std;
const int N = 100;
const int INF = 1e7;
int map[N][N], dist[N], p[N], n, m;
bool flag[N];
void Dijkstra(int u)
{
    
    
	//2.初始化,前驱数组,距离和集合
	for (size_t i = 1; i <= n; i++)
	{
    
    
		dist[i] = map[u][i];//初始化源点到其他点的距离
		flag[i] = false;
		if (dist[i] == INF)//如果源点到其他点存在路径则将前驱设置为源点u
		{
    
    
			p[i] = -1;
		}
		else
		{
    
    
			p[i] = u;
		}
	}
	dist[u] = 0;
	flag[u] = true;//初始化S集合有一个元素:源点u

	//3.求最小,在集合V-S中找到距离源点u最近的顶点t,如果找到则将t加入到集合S中。否则跳出循环
	for (size_t i = 1; i <= n; i++)//初始化V-S集合有n-1个元素
	{
    
    
		int temp = INF, t = u;
		for (size_t j = 1; j <= n; j++)
		{
    
    
			if (!flag[j] && dist[j] < temp)
			{
    
    
				t = j;
				temp = dist[j];//在V-S集合中找到最短距离
			}
		}
		if (t == u) {
    
     return; }
		flag[t] = true;
		//更新V-S集合中与t邻接的顶点
		for (size_t j = 1; j <= n; j++)
			if (!flag[j] && map[t][j] < INF)
				//如果经过t点,路径更短。更新距离和前驱
				if (dist[j] > (dist[t] + map[t][j]))
				{
    
    
					//4.借东风,在已有的t顶点,如果源点经过t到达邻接点j的路径更短。更新矩阵和j的前驱为t
					dist[j] = dist[t] + map[t][j];
					p[j] = t;
				}
	}
}

int main()
{
    
    
	int u, v, w, st;
	system("color 0d");
	cout << "请输入城市的个数:" << endl; cin >> n;
	cout << "请输入城市之间的路线的个数:" << endl; cin >> m;
	cout << "请输入城市之间的路线以及距离:" << endl;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
		{
    
    
			map[i][j] = INF;//初始化邻接矩阵为无穷大
		}
	while (m--)
	{
    
    
		cin >> u >> v >> w;
		map[u][v] = min(map[u][v], w); //邻接矩阵储存,保留最小的距离
	}
	cout << "请输入小明所在的位置:" << endl; ;
	cin >> st;
	Dijkstra(st);
	cout << "小明所在的位置:" << st << endl;
	for (int i = 1; i <= n; i++)
	{
    
    
		cout << "小明:" << st << " - " << "要去的位置:" << i;
		if (dist[i] == INF)
			cout << "sorry,无路可达" << endl;
		else
			cout << " 最短距离为:" << dist[i] << endl;
	}

	//findpath(st);
	return 0;
}

时间复杂度

D i j k s t r a Dijkstra Dijkstra算法中,一共有四个for语句,第一个for语句执行n次。第二个for语句中嵌套了两个for语句,其中嵌套的两个for语句为并行关系(各执行n次)。总的执行次数为 2 n ∗ n + n 2n*n+n 2nn+n,时间复杂度为 O ( n 2 ) O(n^2) O(n2)

猜你喜欢

转载自blog.csdn.net/weixin_42662358/article/details/99819457