最短路径(Dijkstra算法)(c/c++)

Dijkstra是一种求单源点最短路径的算法,即求某一个顶点到其余各顶点之间的最短路径,下面采用邻接矩阵来存储图中信息

算法思路为:

1,假设v0是源点,s是已求得最短路径的终点集合,用D[i]来保存从v0到vi顶点之间的最短路径。初始:将v0置于集合s之中,
2,初始化D[i]为v0到vi之间的权值。a,求从v0出发长度最短的路径,通过计算D[j]=min{D[i]|vi不属于s},找到j顶点,将j顶点加入s集合。b,求下一条最短路径,因为有新的结点加入到s集合中,所以需要修改从v0到其余不属于s的顶点之间的D[i]上的值。即若D[j]+arcs[j][k]<D[k](k不属于s中),修改D[k]值为D[j]+arcs[j][k]
3,重复2中的a,b步骤

示例如下

在这里插入图片描述
按照上面思路列出每一次循环的结果列表如下:

第一次第二次第三次第四次第五次
v1
v210 (v0,v2)\\\\
v360 (v0,v2,v3)50 (v0,v4,v3)\\
v430 (v0,v4)30 (v0,v4)\\\
v5100 (vo,v5)100 (vo,v5)90 (v0,v4,v5)60 (vo,v4,v3,v5\
vjv2v4v3v5\
s{vo,v2}{vo,v2,v4}{vo,v2,v4,v3}{vo,v2,v4,v3,v5}
辅助结构
bool final[maxSize];//表示集合s
final[i] = true;//顶点i在s中
final[i] = false;//i不在s中
bool p[maxSize][maxSize];//表示路径,
p[v][w] = true;//表示w顶点属于从源点到v顶点最短路径上的一个顶点
p[v][w] = false;//表示w顶点不属于从源点到v顶点最短路径上的一个顶点
int D[maxSize];//表示从源点到各个顶点之间最短路径长度
Dijkstra算法
void dijkstra(mGraph& G, int v0)
{
	int i, j, minValue, min;
	//初始化
	for (i = 0; i < G.vexnum; ++i)
	{
		final[i] = false;
		D[i] = G.arc[v0][i];
		for (j = 0; j < G.vexnum; ++j)
			p[v0][j] = false;
		if (G.arc[v0][i] < infini)
		{
			p[i][v0] = true;
			p[i][i] = true;
		}
	}
	
	final[v0] = true;//v0入集合
	D[v0] = 0;
	for (i = 1; i < G.vexnum; ++i)//循环G.vexnum-1次
	{
		//找到最小的D[i]
		minValue = infini;
		for (j = 0; j < G.vexnum; ++j)
		{
			if (final[j]==false)
				if (minValue>D[j])
				{
					minValue = D[j];
					min = j;
				}
		}
		final[min] = true;
		//更新
		for (j = 0; j < G.vexnum; ++j)
		{
			if (final[j] == false && minValue + G.arc[min][j] < D[j])
				//若G.arc[min][j]为INT_MAX,左边相加则会溢出,所以不能使用INT_MAX
			{
				//更新D
				D[j] = minValue + G.arc[min][j];
				//更新p
				for (int temp = 0; temp < G.vexnum; ++temp)
					p[j][temp] = p[min][temp];
				p[j][j] = true;
			}
		}
	}
}
完整示例
#include<iostream>
#define infini INT_MAX/2//最大值不选取INT_MAX,防止溢出
#define maxSize 6//顶点数目
#define MAX 8//边的个数
//邻接矩阵
typedef struct
{
	int vexnum, arcnum;//顶点数和边数
	char vex[maxSize];//顶点信息(字符)
	int arc[maxSize][maxSize];//二维数组(存储边上的信息)
}mGraph;

bool final[maxSize];//表示集合s
bool p[maxSize][maxSize];//表示路径
int D[maxSize];//表示从源点到各个顶点之间最短路径长度

void dijkstra(mGraph& G, int v0);//求最短路径
int main()
{
	using namespace std;
	mGraph G;//邻接矩阵存储图并进行初始化
	G.vexnum = maxSize;
	G.arcnum = MAX;
	char vexData[maxSize] = { 'a', 'b', 'c', 'd', 'e','f' };//顶点信息
	int arcData[MAX][3] = { { 0, 2 ,10}, { 0, 5,100 }, { 0, 4 ,30}, { 1, 2,5 }, { 2, 3 ,50},
					{ 3, 5, 10 }, { 4, 3, 20 }, { 4, 5, 60 } };//连接的边
	int i, j;
	for (i = 0; i < G.vexnum; ++i)
	{
		G.vex[i] = vexData[i];
		for (j = 0; j < G.vexnum; j++)
			G.arc[i][j] = infini;
	}
	for (i = 0; i < G.arcnum; i++)
		G.arc[arcData[i][0]][arcData[i][1]] = arcData[i][2];
	//初始化完毕

	cout << "求从v0顶点出发的最短路径: ";
	cout << endl;
	dijkstra(G, 0);
	for (i = 1; i < G.vexnum; ++i)
	{
		if (D[i] == infini)
		{
			cout << "不存在到v" << i
				<< "的路径" << endl;
			continue;
		}
		cout << "到v" << i << ": 长度为" << D[i] << ' '
			<< "最短路径上的顶点为";
		for (j = 0; j < G.vexnum; ++j)
			if (p[i][j] == true)
				cout << 'v' << j << ' ';
		cout << endl;
	}
	cout << endl;
	return 0;
}
void dijkstra(mGraph& G, int v0)
{
	int i, j, minValue, min;
	//初始化
	for (i = 0; i < G.vexnum; ++i)
	{
		final[i] = false;
		D[i] = G.arc[v0][i];
		for (j = 0; j < G.vexnum; ++j)
			p[v0][j] = false;
		if (G.arc[v0][i] < infini)
		{
			p[i][v0] = true;
			p[i][i] = true;
		}
	}
	
	final[v0] = true;//v0入集合
	D[v0] = 0;
	for (i = 1; i < G.vexnum; ++i)//循环G.vexnum-1次
	{
		//找到最小的D[i]
		minValue = infini;
		for (j = 0; j < G.vexnum; ++j)
		{
			if (final[j]==false)
				if (minValue>D[j])
				{
					minValue = D[j];
					min = j;
				}
		}
		final[min] = true;
		//更新
		for (j = 0; j < G.vexnum; ++j)
		{
			if (final[j] == false && minValue + G.arc[min][j] < D[j])
				//若G.arc[min][j]为INT_MAX,左边相加则会溢出,所以不能使用INT_MAX
			{
				//更新D
				D[j] = minValue + G.arc[min][j];
				//更新p
				for (int temp = 0; temp < G.vexnum; ++temp)
					p[j][temp] = p[min][temp];
				p[j][j] = true;
			}
		}
	}
}

输出结果为下图:
在这里插入图片描述
Floyd算法求最短路径:
https://blog.csdn.net/Little_ant_/article/details/104293812
附:如果采用上面链接博文中的Floyd算法求上图的最短路径,结果如下:
在这里插入图片描述

补充Java代码实现

细节后面在阐述,代码如下:

public class Solution {
    static class State{
        int id; //图节点的 id
        int distFromStart;
        State(int i,int d){
            id=i;
            distFromStart=d;
        }
    }
    int[] d(int start,List<Integer>[] graph){
        int vs=graph.length; // 图中节点的个数
        int[] distTo=new int[vs];//  distTo[i]:表示从 start 到 i 的最短路径权重

        Arrays.fill(distTo, Integer.MAX_VALUE);
        distTo[start]=0;
        Queue<State> pq=new PriorityQueue<>((State a,State b)->{
            return a.distFromStart-b.distFromStart;
        }); // distFromStart 小的排在前面
        pq.offer(new State(start,0));
        while (!pq.isEmpty()){
            State curState=pq.poll();
            int curNodeID=curState.id,curDistFromStart=curState.distFromStart;

            if(curDistFromStart>distTo[curNodeID])  //因为我们会重复添加同一个节点,但是它们的权重是不一样的,权重大的直接被舍弃
                continue;
            for(int nextNodeID:graph[curNodeID]){
                int distToNextNode=distTo[curNodeID]+weight(curNodeID,nextNodeID);
                if(distToNextNode<distTo[nextNodeID]){
                    distTo[nextNodeID]=distToNextNode;
                    pq.offer(new State(nextNodeID,distToNextNode));
                }
            }
        }
        return distTo;
    }
    int weight(int from,int to){
        return 0;//根据具体情况来实现。
    }
}
  • 9
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值