最短路总结 Dijkstra Dijkstra+堆优化 Floyd 单源最短路(Bellman_Ford)SPFA(队列优化)

算法运用到了向前星知识,一种边集数组

https://blog.csdn.net/flymoyu/article/details/90319846

Dijkstra 单源最短路---------权值必须非负

  • 单源最短路径,Dijkstra 算法,邻接矩阵形式,复杂度为O(n^2)
  • 求出源 start到所有点的最短路径,传入图的顶点数,和邻接矩阵 mp[][]
    返回各点的最短路径 lowcost[], 路径 pre[]记录 beg 到 i 路径上的
    父结点,pre[start]=-1
  • 可更改路径权类型,但是权值必须为非负
const int maxn=1010;
const int inf=0x3f3f3f3f;
bool vis[maxn];
int pre[maxn];
void Dijkstra(int mp[][maxn],int dis[],int n,int start)
{
    //初始化
    for(int i=0;i<n;i++)
    {
        dis[i]=inf;
        vis[i]=false;
        pre[i]=-1;
    }
    dis[start]=0;
    for(int j=0;j<n;j++)
    {
        //找到最短路径更新
        int k=-1;
        int minn=inf;
        for(int i=0;i<n;i++)
        {
            if(!vis[i]&&dis[i]<minn)
            {
                minn=dis[i];
                k=i;
            }
        }
        if(k==-1)
            break;
        vis[k]=true;
        for(int i=0;i<n;i++)
        {
            if(!vis[i]&&dis[k]+mp[k][i]<dis[i])
            {
                dis[i]=dis[k]+mp[k][i];
                pre[i]=k;  //路径   记录start到 i 路径上的父亲节点
            }
        }
    }
}

Dijkatra+堆优化
时间复杂度 O(nlong(n))
通过优先队列代替循环找最小值,每次取出的一定是最小值

int dis[maxn];
void Dijkstra(int s)
{
    memset(dis,0x3f,sizeof(dis));
    dis[s]=0;
    priority_queue<int,vector<int>,greater<int>>que;  //升序
    //priority_queue,less>que;
    que.push(make_pair(0,s));
    while(!que.empty())
    {
        int u=que.top().second();
        int d=que.top().first();
        que.pop(); 
        if(dis[u]<d) continue;
        for(int i= head[u];i!=-1;i=e[i].next)
        {
            v=e[i].v;
            w=e[i].w;
            if(dis[v]>d+v)
            {
                dis[v]=d+v;
                que.push(make_pair(dis[v],v));
            }
        }
    }
}

Floyd算法–O(nnn)
核心算法+传递闭包
https://blog.csdn.net/flymoyu/article/details/90298395

核心代码:

for (int k = 1; k <= n; k++) {//n为节点个数
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			if (dis[i][k] + dis[k][j] > dis[i][j])
				dis[i][j] = dis[i][k] + dis[k][j];
		}
	}
}

Bellman_Ford算法

  • 可以处理负边权图。
  • 可以判断是否存在负环回路。
const int inf=0x3f3f3f3f;
const int maxn=550;
int dis[maxn];
struct Edge
{
    int u,v;
    int w;
};
vector<Edge>e;
bool bellman_ford(int s,int n)
{
    for(int i=1;i<=n;i++) dis[i]=inf;
    dis[s]=0;
    for(int i=1;i<n;i++)
    {
        bool flag=false;
        for(int j=0;j<e.size();j++)
        {
            int u=e[j].u;
            int v=e[j].v;
            int w=e[j].w;
            if(dis[v]>dis[u]+w)
            {
                dis[v]=dis[u]+w;
                flag=true;
            }
        }
        if(!flag) return true;
    }
    for(int j=0;j<e.size();j++)
    {
        if(dis[e[j].v]>dis[e[j].u]+e[j].w)
            return false;
    }
    return true;
}

SPFA算法(Bellman_Ford的队列优化)—无法处理带负环的边

算法特点:在 Bellman-ford 算法的基础上加上一个队列优化,减少了冗余的松弛操作,是一种高效的最短路算法。

时间复杂度:O(mn)

主要变量如下:

int n    表示有n个点,从1~n标号

int s,t     s为源点,t为终点

int dis[N]  dis[i]表示源点s到点i的最短路径

int pre[N]  记录路径,pre[i]表示i的前驱结点

bool vis[N] vis[i]=true表示点i在队列中

queue q 队列,在整个算法中有顶点入队了要记得标记vis数组,有顶点出队了记得消除那个标记

【初始化】

dis数组全部赋值为INF,pre数组全部赋值为-1(表示还不知道前驱),

dis[s] = 0 表示源点不要求最短路径(或者最短路径就是0)。

【队列+松弛操作】

读取队头顶点u,并将队头顶点u出队(记得消除标记);将与点u相连的所有点v进行松弛操作,如果能更新估计值(即令d[v]变小),那么就更新,另外,如果点v没有在队列中,那么要将点v入队(记得标记),如果已经在队列中了,那么就不用入队,这样不断从队列中取出顶点来进行松弛操作。

以此循环,直到队空为止就完成了单源最短路的求解。

【算法过程】

设立一个队列用来保存待优化的顶点,优化时每次取出队首顶点 u,并且用 u 点当前的最短路径估计值dis[u]对与 u 点邻接的顶点 v 进行松弛操作,如果 v 点的最短路径估计值dis[v]可以更小,且 v 点不在当前的队列中,就将 v 点放入队尾。这样不断从队列中取出顶点来进行松弛操作,直至队列空为止。

【检测负权回路】

方法:如果某个点进入队列的次数大于等于 n,则存在负权回路,其中 n 为图的顶点数。

const int maxn=1001;
int mp[maxn][maxn];  //邻接矩阵
bool vis[maxn];      //标记数组
int dis[maxn];       //最短距离
int pre[maxn];       //路径
int que_num[maxn];    //入队次数
int n;         //顶点数
int m;       //边数
int s;              //源点
const int inf=0x3f;

bool SPFA()
{
    memset(vis,0,sizeof(vis));
    memset(que_num,0,sizeof(que_num));
    for(int i=0;i<n;i++)
    {
        dis[i]=inf;
        pre[i]=s;
    }
    queue<int>Q;
    Q.push(s);
    dis[s]=0;
    vis[s]=1;
    que_num[s]++;
    while(!Q.empty())
    {
        int u=Q.front();
        Q.pop();
        vis[u]=0;
        for(int v=0;v<n;v++)
        {
            if(mp[u][v]!=inf)
            {
                if(dis[u]+mp[u][v]<dis[v])
                {
                    dis[v]=dis[u]+mp[u][v];
                    pre[v]=u;
                    if(!vis[v])
                    {
                        Q.push(v);
                        que_num[v]++;
                        if(que_num[v]>=n)
                            return false;
                        vis[v]=1;

                    }
                }
            }
        }
    }
    return true;
}

你可能感兴趣的:(图论)