求最短路径

求最短路径的方法:

1.深度优先或广度优先搜索算法

从起点开始访问所有深度遍历路径或广度优先路径,则到达终点节点的路径有多条,取其中路径权值最短的一条则为最短路径。

void dfs(int cur,int dst){
    if(minpath<dst) return;//当前走过的路径大雨之前的最短路径,没有必要再走下去了
    if(cur==en){//临界条件,当走到终点n
       if(minpath>dst){
        minpath=dst;
        return;
       }
    }
     for(int i=1;i<=n;i++){
        if(mark[i]==0&&edge[cur][i]!=inf&&edge[cur][i]!=0){
            mark[i]=1;
            dfs(i,dst+edge[cur][i]);
            mark[i]=0;//需要在深度遍历返回时将访问标志置0
        }
     }
     return;
}

2.Dijkstra算法(迪杰斯特拉算法)
假设存在G=<V,E>,源顶点为V0,U={V0},dist[i]记录V0到i的最短距离,path[i]记录从V0到i路径上的i前面的一个顶点。
1)从V-U中选择使dist[i]值最小的顶点i,将i加入到U中;
2)更新与i直接相邻顶点的dist值。dist[j]=min{dist[j],dist[i]+matrix[i][j]}
3)直到U=V,算法停止。

int dijkstra(int n)
{
	//初始化 v[0]到v[i]的距离
	for(int i=1;i<=n;i++)dis[i]=w[0][i];
	vis[0]=1;//标记v[0]点
	for(int i=1;i<=n;i++)
	{
		//查找最近点
		int min=INF,k=0;
		for(int j=0;j<=n;j++)
			if(!vis[w]&&dis[j]<min)
				min=dis[w],k=j;
		vis[k]=1;//标记查找到的最近点
		//判断是直接v[0]链接v[j]段,还是经过v[k]连接v[j]更短
		for(int j=1;j<=n;j++)
			if(!vis[j]&&min+w[k][j]<dis[j])
				d[j]=min+w[k][j]; 
	 } 
	 return dis[j];
 } 

dijkstra算法是处理单源最短路径的有效算法,但局限于边的权值非负的情况,若图中出现权值为负的边,dijkstra算法就会失效。并且图中不能出现负有向圈。

3.bellman-ford

(1)创建源顶点v到图中所有顶点的距离的集合Distant,且指定一个距离值,源顶点到其本身的距离为0.Distant[i]记录从源点到顶点i的路劲长度

(2)计算最短路径,并进行n-1次遍历(n为顶点数);对于每一条边e=uv,如果Distant[u]+w(uv)<DIstant[v]则Distant[v]=Distant[u]+w(uv),w(uv)为边uv的权值。

(3)如果第(2)步没有对数组集合Distant更新,则说明已经是最短路径,或者部分点不可达。直接执行下一次循环

(4)判断负权回路,若在n-1次遍历之后,仍能对数组更新,则图中存在负环路

核心代码:

for(k = 1; k <= n - 1; k++)
    for(i = 1; i <= m; i++)
        if(dis[v[i]] > dis[u[i]] + w[i])
            dis[v[i]] = dis[u[i]] + w[i];

4.SPFA

SPFA是对bellman-ford的优化,能省去一些冗长的部分,优化时间复杂度。

(1)建立一个队列,初始时队列里只有起始点,再建立一个表格记录起始点到所有点的最短路径(该表格的初始值要赋为极大值,该点到他本身的路径赋为0)。

(2)然后只要队列不为空就取出队首进行松弛;松弛成功后将被松弛的节点(如果不在队列中)加入队列。

松弛操作即为:用该点去刷新起始点到其他点的最短路,如果刷新成功且被刷新点不在队列中则把该点加入到队列最后。

(3)重复执行直到队列为空。

代码实现:

void SPFA(int s)
{
    for(int i=0;i<N;i++) vis[i]=0,d[i]=inf;
    d[s]=0; vis[s]=1;
    queue<int> q;
    q.push(s);
 
    while(!q.empty()){
        int u=q.front();
        q.pop();
        vis[u]=0;
        for(int i=head[u];i!=-1;i=G[i].next){
            int v=E[i].to;
            int w=E[i].w;
            if(d[v]>d[u]+w){
                d[v]=d[u]+w;
                if(!vis[v]){
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
 
}

bellman-ford与SPFA的区别:

5.floyd算法(佛洛依德算法)

从任意节点i到任意节点j的最短路径不外乎2种可能,一是直接从i到j,二是从i经过若干个节点k到j。我们假设Dis(i,j)为节点u到节点v的最短路径的距离,对于每一个节点k,我们检查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立,如果成立,证明从i到k再到j的路径比i直接到j的路径短,我们便设置Dis(i,j) = Dis(i,k) + Dis(k,j),这样一来,当我们遍历完所有节点k,Dis(i,j)中记录的便是i到j的最短路径的距离。

用dist[v][w]记录每一对顶点的最短距离。dist为距离数组,path为路径数组

//Floyd算法(多源最短路径算法) 
bool Floyd(){
	for(int k = 1 ; k < this->Nv+1 ; k++){	//k代表中间顶点 
		for(int i = 1  ; i < this->Nv+1 ; i++){//i代表起始顶点 
			for(int j = 1 ; j < this->Nv+1 ; j++){//j代表终点 
				if(this->dist[i][k] + this->dist[k][j] < this->dist[i][j]){
					this->dist[i][j] = this->dist[i][k] + this->dist[k][j];
					if(i == j && this->dist[i][j] < 0){//发现了负值圈 
						return false;
					}
					this->path[i][j] = k;
				}					
			}
		}
	}
	return true; 
}

上述函数在一个图类中进行定义,dist和path均为私有变量,dist为距离数组,path为路径数组。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值