迪杰斯特拉算法(可打印最短路径)

本文介绍了一种使用迪杰斯特拉算法寻找有向图中最短路径的方法,并提供了详细的C++实现代码。通过构建邻接矩阵来表示图的结构,算法能够有效地计算出任意两点间的最短路径及其长度。

转自:迪杰斯特拉算法(可打印最短路径)

#include <iostream>
#include <iomanip>
#include <string>
using namespace std;

#define INFINITY 65535//无边时的权值
#define MAX_VERTEX_NUM 10//最大顶点数

typedef struct MGraph{
	string vexs[10];//顶点信息
	int arcs[10][10];//邻接矩阵
	int vexnum, arcnum;//顶点数和边数
}MGraph;

int LocateVex(MGraph G, string u)//返回顶点u在图中的位置
{
	for(int i=0; i<G.vexnum; i++)
		if(G.vexs[i]==u)
			return i;
	return -1;
}

void CreateDN(MGraph &G)//构造有向网
{
	string v1, v2;
	int w;
	int i, j, k;
	cout<<"请输入顶点数和边数:";
	cin>>G.vexnum>>G.arcnum;

	cout<<"请输入顶点:";
	for(i=0; i<G.vexnum; i++)
		cin>>G.vexs[i];

	for(i=0; i<G.vexnum; i++)
		for(j=0; j<G.vexnum; j++)
			G.arcs[i][j]=INFINITY;

	cout<<"请输入边和权值:"<<endl;
	for(k=0; k<G.arcnum; k++)
	{
		cin>>v1>>v2>>w;
		i=LocateVex(G, v1);
		j=LocateVex(G, v2);
		G.arcs[i][j]=w;
	}
}

//迪杰斯特拉算法求有向网G的v0顶点到其余顶点v的最短路径p[v]及带权长度D[v]
//p[][]=-1表示没有路径,p[v][i]存的是从v0到v当前求得的最短路径经过的第i+1个顶点(这是打印最短路径的关键),则v0到v的最短路径即为p[v][0]到p[v][j]直到p[v][j]=-1,路径打印完毕。
//final[v]为true当且仅当v∈S,即已经求得从v0到v的最短路径。
void ShortestPath_DIJ(MGraph G, int v0, int p[][MAX_VERTEX_NUM], int D[])
{
	int v, w, i, j, min;
	bool final[10];
	
	for(v=0; v<G.vexnum; v++)
	{
		final[v]=false;//设初值
		D[v]=G.arcs[v0][v];//D[]存放v0到v得最短距离,初值为v0到v的直接距离
		for(w=0; w<G.vexnum; w++)
			p[v][w]=-1;//设p[][]初值为-1,即没有路径
		if(D[v]<INFINITY)//v0到v有直接路径
		{
			p[v][0]=v0;//v0到v最短路径经过的第一个顶点
			p[v][1]=v;//v0到v最短路径经过的第二个顶点
		}
	}
	
	D[v0]=0;//v0到v0距离为0
	final[v0]=true;//v0顶点并入S集
	
	for(i=1; i<G.vexnum; i++)//其余G.vexnum-1个顶点
	{//开始主循环,每次求得v0到某个顶点v的最短路径,并将v并入S集,然后更新p和D
		min=INFINITY;
		for(w=0; w<G.vexnum; w++)//对所有顶点检查
			if(!final[w] && D[w]<min)//在S集之外(即final[]=false)的顶点中找离v0最近的顶点,将其赋给v,距离赋给min
			{
				v=w;
				min=D[w];
			}
			final[v]=true;//v并入S集
			for(w=0; w<G.vexnum; w++)//根据新并入的顶点,更新不在S集的顶点到v0的距离和路径数组
			{
				if(!final[w] && min<INFINITY && G.arcs[v][w]<INFINITY && (min+G.arcs[v][w]<D[w]))
				{//w不属于S集且v0->v->w的距离<目前v0->w的距离
					D[w]=min+G.arcs[v][w];//更新D[w]
					for(j=0; j<G.vexnum; j++)//修改p[w],v0到w经过的顶点包括v0到v经过的所有顶点再加上顶点w
					{
						p[w][j]=p[v][j];
						if(p[w][j]==-1)//在p[w][]第一个等于-1的地方加上顶点w
						{
							p[w][j]=w;
							break;
						}
					}					
					
				}
			}
	}	
}

void main()
{
	int i, j;
	MGraph g;
	CreateDN(g);
	int p[MAX_VERTEX_NUM][MAX_VERTEX_NUM];//最短路径数组p
	int D[MAX_VERTEX_NUM];//最短距离数组D
	ShortestPath_DIJ(g, 0, p, D);
	
	cout<<"最短路径数组p[i][j]如下:"<<endl;
	for(i=0; i<g.vexnum; i++)
	{
		for(j=0; j<g.vexnum; j++)
			cout<<setw(3)<<p[i][j]<<" ";
		cout<<endl;
	}

	cout<<g.vexs[0]<<"到各顶点的最短路径及长度为:"<<endl;
	for(i=0; i<g.vexnum; i++)
	{
		if(i!=0 && D[i]!=INFINITY)
		{
			cout<<g.vexs[0]<<"-"<<g.vexs[i]<<"的最短路径长度为:"<<D[i];
			cout<<"  最短路径为:";
			for(j=0; j<g.vexnum; j++)
			{
				if(p[i][j]>-1)
					cout<<g.vexs[p[i][j]]<<" ";
			}
			cout<<endl;			
		}
		else if(D[i]==INFINITY)
			cout<<g.vexs[0]<<"-"<<g.vexs[i]<<":"<<"不可达"<<endl;
	}

}



<think>我们被要求使用C语言实现Dijkstra算法求解单源最短路径问题。 根据引用[1]:Dijkstra算法是一种贪心算法,用于在带权图中从一个源点找到所有其他顶点的最短路径。它要求图中边的权重非负。 引用[2]描述了Dijkstra算法类似于Prim算法,它维护两个集合:一个是最短路径树(SPT)的顶点集合,另一个是尚未包含在SPT中的顶点集合。在每一步中,从尚未包含的顶点集合中选取一个距离源点最近的顶点加入SPT。 引用[3]提到了Dijkstra算法的详细步骤。 引用[4]说明Dijkstra算法适用于有向图和无向图,但要求边权非负。 引用[5]给出了Dijkstra算法的伪代码。 我们将按照以下步骤实现: 1. 初始化:将源点的距离设为0,其他所有顶点的距离设为无穷大(用一个大数表示),并将所有顶点标记为未访问。 2. 使用一个优先队列(最小堆)来高效地选取当前距离最小的顶点。但C标准库中没有优先队列,因此我们可以使用数组来存储,每次遍历数组来选取最小距离的顶点(适用于顶点数不多的情况)。 3. 对于当前处理的顶点u,遍历其所有邻接顶点v,如果通过u到v的路径比当前已知的v的距离更短,则更新v的距离(松弛操作)。 4. 重复上述过程,直到所有顶点都被访问。 由于C语言没有内置的优先队列,我们将使用数组实现。如果顶点数很大,效率可能较低(O(V^2)),但这是最基础的实现方式。 假设图的表示:使用邻接矩阵。顶点数量为V,源点为s。 步骤: 1. 创建大小为V的数组dist,存储从源点到每个顶点的最短距离。 2. 创建大小为V的数组visited,标记顶点是否已处理。 3. 初始化dist:dist[s] = 0,其他为无穷大(例如INT_MAX)。 4. 初始化visited:全部为false(0)。 5. 循环V次: a. 从未处理的顶点中选取dist最小的顶点u(如果使用数组遍历,复杂度为O(V))。 b. 标记u为已访问。 c. 遍历u的所有邻接点v(即遍历所有顶点,检查邻接矩阵中是否有边): - 如果存在边(graph[u][v]!=0且graph[u][v]不为无穷)且visited[v]为false,并且dist[u] + graph[u][v] < dist[v],则更新dist[v] = dist[u] + graph[u][v]。 6. 输出dist数组,即源点到各顶点的最短距离。 注意:如果图是稀疏图,使用邻接表会更高效,但这里我们先使用邻接矩阵。 下面给出C语言代码实现: 注意:我们假设图中没有负权边,且图是连通的(或者非连通则不可达的顶点距离保持无穷大)。 代码实现: */ #include <stdio.h> #include <limits.h> // 定义顶点个数 #define V 9 // 辅助函数:找到当前未处理顶点中距离最小的顶点 int minDistance(int dist[], int visited[]) { int min = INT_MAX, min_index; for (int v = 0; v < V; v++) { if (visited[v] == 0 && dist[v] <= min) { min = dist[v]; min_index = v; } } return min_index; } // 打印最短距离 void printSolution(int dist[]) { printf("Vertex \t Distance from Source\n"); for (int i = 0; i < V; i++) printf("%d \t\t %d\n", i, dist[i]); } // Dijkstra算法实现(邻接矩阵表示) void dijkstra(int graph[V][V], int src) { int dist[V]; // 存储最短距离 int visited[V]; // 记录顶点是否已处理 // 初始化所有距离为无穷大,visited为0(未访问) for (int i = 0; i < V; i++) { dist[i] = INT_MAX; visited[i] = 0; } // 源点到自己的距离为0 dist[src] = 0; // 循环V-1次,因为源点已经处理,剩下V-1个顶点 for (int count = 0; count < V - 1; count++) { // 选取未处理顶点中最小距离的顶点 int u = minDistance(dist, visited); visited[u] = 1; // 标记为已处理 // 更新u的所有邻接点的距离 for (int v = 0; v < V; v++) { // 如果v未被访问,且u到v有边(graph[u][v]非0),且从源点到u的距离不是无穷大(否则相加会溢出), // 并且通过u到v的距离比当前记录的距离小,则更新 if (!visited[v] && graph[u][v] && dist[u] != INT_MAX && dist[u] + graph[u][v] < dist[v]) { dist[v] = dist[u] + graph[u][v]; } } } // 打印最短距离 printSolution(dist); } // 测试代码 int main() { // 用邻接矩阵表示图,0表示没有直接边 int graph[V][V] = { {0, 4, 0, 0, 0, 0, 0, 8, 0}, {4, 0, 8, 0, 0, 0, 0, 11, 0}, {0, 8, 0, 7, 0, 4, 0, 0, 2}, {0, 0, 7, 0, 9, 14, 0, 0, 0}, {0, 0, 0, 9, 0, 10, 0, 0, 0}, {0, 0, 4, 14, 10, 0, 2, 0, 0}, {0, 0, 0, 0, 0, 2, 0, 1, 6}, {8, 11, 0, 0, 0, 0, 1, 0, 7}, {0, 0, 2, 0, 0, 0, 6, 7, 0} }; dijkstra(graph, 0); return 0; } /* 说明: 1. 该实现使用邻接矩阵,因此空间复杂度为$O(V^2)$,时间复杂度为$O(V^2)$(因为每次选取最小距离需要$O(V)$,总共$V$次,而更新邻接点需要遍历所有顶点,也是$O(V)$,所以总时间$O(V^2)$)。 2. 对于稀疏图,可以使用邻接表配合优先队列(最小堆)来优化,选取最小距离的操作可以降到$O(\log V)$,总时间复杂度为$O((V+E)\log V)$。但在C语言中,需要自己实现堆,这里为了简单,使用数组实现。 注意:如果图中存在负权边,Dijkstra算法无法正确工作,需要使用Bellman-Ford算法。 相关问题: 1. 如何修改上述代码以支持有向图? 2. 如果图是稀疏图,如何优化Dijkstra算法的时间复杂度? 3. 如何输出最短路径(而不仅仅是距离)? 4. 如果图中存在负权边,应该使用什么算法? 5. Dijkstra算法和Bellman-Ford算法有什么区别? 引用说明: [^1]: 引用1说明Dijkstra算法使用优先队列实现高效。 [^2]: 引用2描述了Dijkstra算法维护两个集合。 [^3]: 引用3提到Dijkstra算法的详细步骤。 [^4]: 引用4说明Dijkstra算法适用于非负权图。 [^5]: 引用5给出了Dijkstra算法的伪代码。 */</think>### Dijkstra算法C语言实现 以下是使用C语言实现Dijkstra算法求解单源最短路径的完整代码: ```c #include <stdio.h> #include <limits.h> #include <stdbool.h> #define V 6 // 顶点数量 // 找到当前未处理顶点中距离最小的顶点 int minDistance(int dist[], bool visited[]) { int min = INT_MAX, min_index; for (int v = 0; v < V; v++) { if (!visited[v] && dist[v] <= min) { min = dist[v]; min_index = v; } } return min_index; } // 打印最短路径结果 void printSolution(int dist[]) { printf("Vertex \t Distance from Source\n"); for (int i = 0; i < V; i++) printf("%d \t\t %d\n", i, dist[i]); } // Dijkstra算法实现 void dijkstra(int graph[V][V], int src) { int dist[V]; // 存储最短距离 bool visited[V]; // 记录顶点是否已处理 // 初始化所有距离为无穷大,visited为false for (int i = 0; i < V; i++) { dist[i] = INT_MAX; visited[i] = false; } dist[src] = 0; // 源点到自身的距离为0 // 循环处理所有顶点 for (int count = 0; count < V - 1; count++) { // 选取未处理顶点中最小距离的顶点 int u = minDistance(dist, visited); visited[u] = true; // 标记为已处理 // 更新u的所有邻接点的距离 for (int v = 0; v < V; v++) { // 如果v未被访问,且u到v有边,且从源点到u的距离不是无穷大 // 且通过u到v的距离比当前记录的距离小 if (!visited[v] && graph[u][v] && dist[u] != INT_MAX && dist[u] + graph[u][v] < dist[v]) { dist[v] = dist[u] + graph[u][v]; } } } printSolution(dist); } int main() { // 图的邻接矩阵表示(0表示没有直接边) int graph[V][V] = { {0, 2, 0, 0, 0, 5}, {2, 0, 4, 1, 0, 0}, {0, 4, 0, 3, 6, 0}, {0, 1, 3, 0, 2, 0}, {0, 0, 6, 2, 0, 3}, {5, 0, 0, 0, 3, 0} }; dijkstra(graph, 0); // 从顶点0开始计算最短路径 return 0; } ``` ### 代码说明: 1. **数据结构**:使用邻接矩阵`graph[V][V]`表示图,其中`V`是顶点数 2. **核心变量**: - `dist[]`:存储从源点到各顶点的最短距离 - `visited[]`:标记顶点是否已处理 3. **算法流程**: - 初始化所有距离为无穷大(`INT_MAX`),源点距离为0 - 循环选择未访问顶点中距离最小的顶点`u` - 更新`u`的所有邻接点`v`的距离:$dist[v] = min(dist[v], dist[u] + graph[u][v])$ - 重复直到所有顶点被处理 ### 算法特点: 1. **时间复杂度**:$O(V^2)$(使用邻接矩阵) 2. **空间复杂度**:$O(V)$ 3. **适用条件**:仅适用于非负权重的图[^4] ### 示例输出: ``` Vertex Distance from Source 0 0 1 2 2 6 3 3 4 5 5 5 ``` ### 优化方向: 1. **优先队列优化**:使用最小堆可将时间复杂度优化到$O((V+E)\log V)$[^1] 2. **邻接表存储**:对稀疏图更高效 3. **路径记录**:添加`parent[]`数组可回溯完整路径
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值