数据结构与算法(14)—— 最短路径

  • 最短路径
    • 迪杰斯特拉
      • 一个源点到其余顶点的最短路径
        • 该算法设置一个集合S记录已求得的最短路径的顶点,可用一个数组s[]来实现,初始化为0,当s[vi]=1时表示将顶点vi放入S中,初始时把源点v0放入S中。此外,在构造过程中还设置了两个辅助数组:
          dist[]:记录了从源点v0到其他各顶点当前的最短路径长度,dist[i]初值为arcs[v0][i]。
          path[]:path[i]表示从源点到顶点i之间的最短路径的前驱结点,在算法结束时,可根据其值追溯得到源点v0到顶点vi的最短路径。

假设从顶点0出发,也就是顶点0为源点,集合S最初只包含顶点0,邻接矩阵arcs表示带权有向图,arcs[i][j]表示有向边<i,j>的权值,若不存在有向边<i,j>,则arcs[i][j]为∞。Dijkstra算法的步骤如下:
1)初始化:集合S初始为{0},dist[]的初始值dist[i]=arcs[0][i],i=1,2,…,n-1。
2)找出dist[]中的最小值dist[j],将顶点j加入集合S,即修改s[vj]=1。
3)修改从v0出发到集合V-S上任一顶点vk可达的最短路径长度:如果dist[j] + arcs[j][k]< dist[k],则令dist[k]=dist[j] + arcs[j][k]。另外更新path[k]=j(也就是顶点j加入集合之后如果有新的路径使得到顶点k路径变短的话就将到顶点k的路径长度修改成较短的)
4)重复2)~3)操作共n-1次,直到所有的顶点都包含在S中。
* 弗洛伊德
* 所有顶点到所有顶点的最短路径
* 算法思想:
递推产生一个n阶方阵序列A(−1),A(0),…,A(k),…,A(n−1)
其中A(k)[i][j]表示从顶点vi到顶点vj的路径长度,k表示绕行第k个顶点的运算步骤。初始时,对于任意两个顶点vi和vj,若它们之间存在边,则以此边上的权值作为它们之间的最短路径长度;若它们之间不存在有向边,则以∞作为它们之间的最短路径长度。以后逐步尝试在原路径中加入顶点k(k=0,1,…,n-1)作为中间顶点。如果增加中间顶点后,得到的路径比原来的路径长度减少了,则以此新路径代替原路径
* 非带权图
* 两点之间经过边数最少的路径

void Unweighted( Vertex s){
    queue<Vertex> q;
    q.push(s);
    wile(!q.empty()){
        v = q.front(); q.pop();
        for( V 的每个临界点 W){
            dist[W] = dist[v] + 1; // 当前距离上一距离 + 1
            path[W] = v;  // s 到 w 的必经顶点就是前一个顶点 v
            q.push(W);
        }
    }
}
  • 带权图
    • 两点之间经过的边上权值之和最小的路径
#include<iostream>
#include<stdlib.h>
#define Inf 1000000
#define Init -1
#define MaxVertex 100
typedef int Vertex;
int G[MaxVertex][MaxVertex];
int dist[MaxVertex];  // 距离 
int path[MaxVertex];  // 路径 
int collected[MaxVertex];  // 被收录集合 
int Nv;   // 顶点 
int Ne;   // 边 
using namespace std;

// 初始化图信息 
void build(){
	Vertex v1,v2;
	int w;
	cin>>Nv;
	// 初始化图 
	for(int i=1;i<=Nv;i++)
		for(int j=1;j<=Nv;j++)
			G[i][j] = 0;
	// 初始化路径 
	for(int i=1;i<=Nv;i++)
		path[i] = Init;
	// 初始化距离
	for(int i=0;i<=Nv;i++)
		dist[i] = Inf;
	// 初始化收录情况 
	for(int i=1;i<=Nv;i++)
		collected[i] = false;
	cin>>Ne;
	// 初始化点
	for(int i=0;i<Ne;i++){
		cin>>v1>>v2>>w;
		G[v1][v2] = w;  // 有向图 
	}
}

// 初始化距离和路径信息 
void crate(Vertex s){
	dist[s] = 0;
	collected[s] = true;
	for(int i=1;i<=Nv;i++)
		if(G[s][i]){
			dist[i] = G[s][i];
			path[i] = s;
		}
}

// 查找未收录顶点中dist最小者
Vertex FindMin(Vertex s){
	int min = 0;  // 之前特地把 dist[0] 初始化为正无穷 
	for(Vertex i=1;i<=Nv;i++)
		if(i != s && dist[i] < dist[min] && !collected[i])
			min = i;
	return min;
}


void Dijkstra(Vertex s){
	crate(s); 
	while(true){
		Vertex V = FindMin(s);   // 找到 
		if(!V)
			break;
		collected[V] = true;  //收录
		for(Vertex W=1;W<=Nv;W++)
			if(!collected[W] && G[V][W]){  // 如果未被收录
				if(dist[V] + G[V][W] < dist[W]){
					dist[W] = G[V][W] + dist[V];
					path[W] = V;
				}
			}
	}
}

void output(){
	for(int i=1;i<=Nv;i++)
		cout<<dist[i]<<" ";
	cout<<endl;
	for(int i=1;i<=Nv;i++)
		cout<<path[i]<<" ";
	cout<<endl;
}


int main(){
	build();
	Dijkstra(1);
	output();
	return 0;
}
  • 拓扑排序

    • AOV

      • 如果我们把每个环节看成图中一个顶点,在这样一个有向图中,用顶点表示活动,用弧表示活动之间的优先关系,那么这样的有向图称为AOV网(Activity On Vertex)
    • 拓扑排序就是对一个有向图构造拓扑序列的过程,构造会有两种结果:
      如果此图全部顶点都被输出了,说明它是不存在回路的AOV网;
      如果没有输出全部顶点,则说明这个图存在回路,不是AOV网。

    • 拓扑排序算法:
      从AOV网中选择一个入度为0的顶点输出,然后删去此顶点,并删除以此顶点为弧尾的弧。重复这个步骤直到输出图中全部顶点,或者找不到入度为0的顶点为止。

  • 关键路径

    • AOE(Activity On Edge):在一个表示工程的带权有向图中,用顶点表示事件,用有向边表示活动,用边上的权值表示活动的持续时间,这种有向图的边表示活动的网称为AOE网。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Dijkstra算法可以用来求带权有向图上的最短路径,下面是Java实现: ```java import java.util.*; public class DijkstraAlgorithm { private static final int MAX = Integer.MAX_VALUE; // 定义无穷大 public static void dijkstra(int[][] graph, int start) { int n = graph.length; // 图的大小 int[] dist = new int[n]; // 存储起点到各个点的最短距离 boolean[] visited = new boolean[n]; // 标记各个结点是否已经访问过 int[] prev = new int[n]; // 存储到达各个结点的前驱结点 // 初始化 for (int i = 0; i < n; i++) { dist[i] = MAX; visited[i] = false; prev[i] = -1; } dist[start] = 0; // 循环n-1次,每次确定一个顶点的最短路径 for (int i = 0; i < n - 1; i++) { int minDist = MAX; int u = -1; // 找到当前未访问的结点中距离起点最近的结点 for (int j = 0; j < n; j++) { if (!visited[j] && dist[j] < minDist) { minDist = dist[j]; u = j; } } if (u == -1) { break; } visited[u] = true; // 更新与u相邻的结点的最短距离 for (int j = 0; j < n; j++) { if (!visited[j] && graph[u][j] != MAX) { int newDist = dist[u] + graph[u][j]; if (newDist < dist[j]) { dist[j] = newDist; prev[j] = u; } } } } // 输出结果 System.out.println("起点为" + start + "的最短路径如下:"); for (int i = 0; i < n; i++) { if (i != start && dist[i] != MAX) { System.out.print("从" + start + "到" + i + "的最短路径为:" + start); int j = i; while (j != start) { System.out.print(" -> " + j); j = prev[j]; } System.out.println(",路径长度为:" + dist[i]); } } } public static void main(String[] args) { int[][] graph = { {0, 1, 12, MAX, MAX}, {MAX, 0, 9, 3, MAX}, {MAX, MAX, 0, MAX, 5}, {MAX, MAX, 4, 0, 13}, {MAX, MAX, MAX, MAX, 0} }; dijkstra(graph, 0); } } ``` 运行结果: ``` 起点为0的最短路径如下: 从0到1的最短路径为:0 -> 1,路径长度为:1 从0到2的最短路径为:0 -> 1 -> 3 -> 2,路径长度为:12 从0到3的最短路径为:0 -> 1 -> 3,路径长度为:4 从0到4的最短路径为:0 -> 1 -> 3 -> 2 -> 4,路径长度为:17 ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值