数据结构——图(2)

本文介绍了在有向图中寻找最短路径的几种方法,包括无权图的BFS,Dijkstra算法处理单源最短路径(考虑非负权重),Floyd算法用于每对点最短路径,以及Bellman-Ford算法,后者可以检测负权回路。提供了相应的算法实现和复杂度分析。
摘要由CSDN通过智能技术生成

最短路径:

给定连通有向图 G 及其中的顶点 u v, 找到从 u v 的最短路径及其长度。
无权图的最短路径:两点间边数最少(BFS)
有向带权图:加起来权重最小,分为单源最短路径、每对点最短路径。

Dijkstra算法(解决单源最短路径)

条件:每条边的权重非负

单源最短路径:给定源顶点s,求它到其他任意顶点的最短路径

算法:

s:已求出最短路径的顶点集合

L=V-S;每个步骤从L选取一个顶点v加入S

贪心准则:vL中距s距离最短者;新最短路径=已有最短路径+一条边
每个顶点无需保存其完整路径,保存路径中它的前一顶点即可

实现:
数组p:保存路径(p[i]:最短路径中,顶点i的前驱顶点)

数组d:当前最短路径长度

i在s中:真正的最短路径

i在L中:当前的最短路径

代码:

#include<iostream>
#include<stack>
#include<vector>
#define INF 0x3f3f3f3f
using namespace std;

int main()
{
	int n, m;
	cin >> n >> m;
	int(*path)[20] = new int[20][20];
	int* dist = new int[20];//d[i]为从root到i的最短距离
	int* visit = new int[20];//i是否已经访问

	vector<int> pre(20);//记录前一个点
	for (int i = 0; i < n; i++)
		pre[i] = i;

	while (m--)
	{
		int i, j, k;
		cin >> i >> j >> k;
		path[i][j] = k;
	}
	int root;
	cin >> root;
	memset(dist, INF, sizeof(dist));
	dist[root] = 0;
	memset(visit, 0, sizeof(visit));
	

	//主要算法区域
	for (int i = 1; i <= n; i++)
	{
		int j = 0;//遍历,找到当前距地最短的点j
		for (int k = 1; k <= n; k++)
		{
			if (!visit[k] && dist[k] <= dist[j])
				j = k;
		}
		visit[j] = 1;
		
		for (int k = 1; k <= n; k++)
		{
			if (dist[k] > dist[j] + path[j][k])
			{
				dist[k] = dist[j] + path[j][k];
				pre[k] = j;
			}
			//以j为起点更新附近的点
		}
	}
	for (int i = 1; i < n; i++)
		cout << dist[i] << " ";
}

Floyd算法(每一对点的最短路径)

顶点编号为 1 n
c(i,j,k) i-> j 的“最短路径”长度 —— 加了限制条件,路径中顶点的最大编号为 k(除开起终点)
存在边<i, j>  则  c(i, j, 0)=<i, j> 的长度
不存在边<i, j>,则 c(i, j, 0)=+
c(i, i, 0)=0;c(i, j, n)——最短路径长度

计算c(i,j,k):

路径中无k: c1(i,j,k)=c(i,j,k-1)

路径中有k: c2(i,j,k)=c(i,k,k-1)+c(k,j,k-1) 即两段的最小路径

于是:c(i,j,k)=min{c1,c2}

复杂度分析:递归算法  O(n*2^n); 迭代计算 O(n^3)

OJ-(floyd算法实现)

在oj基础上实现了打印经过点

#include<iostream>
#include<cstring>
#include <time.h>
#define inf 0x3f3f3f3f
using namespace std;
//打印路径
void print_pass(int pass[20][20],int i, int j)
{
	if (i == j)return;
	if (pass[i][j] == -1)
		cout << i << "->";
	//(pass=-1即中间没经过点)

	else//递归
	{
		print_pass(pass, i, pass[i][j]);
		print_pass(pass,pass[i][j],j );
	}
}
int main()
{
	int n, m;
	cin >> n >> m;
	//初始化
	int(*num)[20] = new int[20][20];//(储存路径)
	int(*pass)[20] = new int[20][20];//储存经过的最大数
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n; j++)
		{
			pass[i][j] = -1;
			if (j == i)
				num[i][j] = 0;
			else
				num[i][j] = inf;
		}
	}

	for (int i = 0; i < m; i++)
	{
		int x, y, k;
		cin >> x >> y >> k;
		num[x][y] = k;
	}
//主体部分如下!
	for (int k = 0; k < n; k++)//中间能经过的最大数
	{
		for (int i = 0; i < n; i++)
		{
			for (int j = 0; j < n; j++)
			{
				if (num[i][j]>(num[i][k] + num[k][j]))
				{
					num[i][j] = num[i][k] + num[k][j];
					pass[i][j] = k;//最后的k是最大的k
				}
			}
		}
	}
	cout << endl;
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n; j++)
		{
			if (num[i][j] == inf)
				cout << -1 << " ";
			else
			{
				cout << num[i][j] << " ";
				print_pass(pass, i, j);
				cout << j << endl;
			}
		}
	}
	return 0;
}

Bellman算法(可以检测负权回路的单源最短路径算法)

Bellman-ford算法详解_bellmanford算法-CSDN博客

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值