对求最短路径常见算法的简单总结

最近正好在学最短路径,借此机会把学到的知识总结下,一来分享给大家阅读,二来方便以后自己查阅。

先列一下下面会总结到的求最短路径的算法:

1.Dijkstra算法;

2.Bellman-Ford算法;

3.Folyd算法;

4.SPFA算法;


Dijkstra算法:求单源最短路径(不带负权重的环)


step1.初始化,dis[V0]=0,dis[i]=无穷大(i≠V0,表示不可达);

step2.从V-U中选择使dis[i]值最小的顶点i,将i加入到U中;

step3.更新与i直接相邻顶点的dis值(dis[j]=min{dis[j],dis[i]+cost[i][j]})。

step4.重复step2和step3,直到U=V,停止。


#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;
#define MAXN 10000
const int INF=0x3f3f3f3f;
int n, m, ori;
int dis[MAXN], pre[MAXN];
int cost[MAXN][MAXN];

struct node
{
    int v;
    int len;
    node(){}
    node(int vv, int ll):v(vv), len(ll){}
    bool operator < (const node& a) const
    {
        return len > a.len;
    }
};

void Dijkstra()
{
    for(int i = 1; i <= n; i++)
        dis[i] = INF;
    priority_queue q;//最小优先队列优化
    q.push(node(ori, 0));
    dis[ori] = 0;
    while(!q.empty())
    {
        node t;
        t = q.top();
        q.pop();
        int now = t.v;
        for(int j = 1; j <= n; j++)
        {
            if(cost[now][j] != INF && dis[j] > dis[now] + cost[now][j])
            {
                dis[j] = dis[now] + cost[now][j];
                pre[j] = now;
                q.push(node(j, dis[j]));
            }

        }

    }

}

void print_path(int pos)
{
    printf("%d\n", dis[pos]);
    while(ori != pos)
    {
        printf("%d<---", pos);
        pos = pre[pos];
    }
    printf("%d\n", ori);

}

int main()
{
    scanf("%d%d%d", &n, &m, &ori);
    int u, v, w;
    memset(cost, INF, sizeof(cost));
    while(m--)
    {
        scanf("%d%d%d", &u, &v, &w);
        cost[u][v] = w;
        cost[v][u] = w;//若是有向图则不要这行
    }
    Dijkstra();
    for(int i = 1; i <= n; i++)
    {
        printf("%d to %d: ", ori, i);
        if(dis[i] != INF)
            print_path(i);
        else
            printf("Have no path\n");
    }
    return 0;
}



Bellman-Ford算法:求单源最短路径(可带负权重的环)

假设存在G=,源顶点为V0,U={V0},dis[i]记录V0到i的最短距离,pre[i]记录从V0到i路径上的i前面的一个顶点。

step1.初始化,dis[V0]=0,dis[i]=无穷大(i≠V0,表示不可达);

step2.进行循环,循环下标为从0到V-1(V等于图中点的个数),在循环内部,遍历所有的边,进行松弛操作;

step3.遍历图中所有的边(edge(u,v)),判断是否存在这样情况:d(v) > d (u) + w(u,v),存在则返回false,表示图中存在从源点可达的权为负的回路。


#include 
#include 
#include 
#include 

using namespace std;

#define MAXN 10000
const int INF = 0x3f3f3f3f;

struct Edge
{
    int u, v;//u->v
    int w;//距离
};

int n, m, ori;//点数、边数、起始点
int dis[MAXN], pre[MAXN];
vector edge;

bool Bellman_Ford()
{
    for(int i = 1; i <= n; i++)
        dis[i] = INF;
    dis[ori] = 0;
    for(int i = 0; i < n-1; i++)
        for(int j = 0; j < edge.size(); j++)
        {
            if(dis[edge[j].v] > dis[edge[j].u] + edge[j].w)
            {
                dis[edge[j].v] = dis[edge[j].u] + edge[j].w;
                pre[edge[j].v] = edge[j].u;
            }

        }

    bool flag = true;
    for(int i = 0; i < edge.size(); i++)
    {
        if(dis[edge[i].v] > dis[edge[i].u] + edge[i].w)
        {
            flag = false;
            break;
        }
    }
    return flag;

}

void print_path(int start)//打印路径
{
    printf("%d\n", dis[start]);
    while(start != ori)
    {
        printf("%d<---", start);
        start = pre[start];
    }
    printf("%d\n", ori);
}


int main()
{
    scanf("%d%d%d", &n, &m, &ori);
    Edge t;
    while(m--)
    {
        scanf("%d%d%d", &t.u, &t.v, &t.w);
        edge.push_back(t);
    }
    if(Bellman_Ford())
    {
        for(int i = 1; i <= n; i++)
        {
            printf("%d to %d: ", ori, i);
            if(dis[i] != INF)
                print_path(i);
            else
                printf("Have no path\n");
        }
    }
    else
        printf("This map has negative circle\n");
    return 0;
}

Folyd算法:所有顶点对最短路径(不带负权重的环)

假设存在G=,dis[i][j]记录i到j的最短距离,path[i][j]记录从i到j路径上j的前面的一个顶点。

step1.将所有顶点对之间距离初始化为无穷大(dis[i][j]=无穷大),然后将输入的i到j的距离保存在dis[i][j]中;

step2.依次扫描每一个点,并以其为基点再遍历所有每一对顶点dis[i][j]的值,看看是否可用过该基点让这对顶点间的距离更小。


#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;
#define MAXN 10000
const int INF = 0x3f3f3f3f;

int dis[MAXN][MAXN], path[MAXN][MAXN];
int n, m, ori;

void Floyd()
{
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
            path[i][j] = i;
    for(int k = 1; k <= n; k++)
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= n; j++)
    {
        if(dis[i][j] > dis[i][k] + dis[k][j])
        {
            dis[i][j] = dis[i][k] + dis[k][j];
            path[i][j] = path[k][j];
        }
    }

}

void prin_path()
{
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
    {
        if(i != j)
        {
            if(dis[i][j] == INF)
                printf("have no path\n");
            else
            {
                printf("%d to %d: ", i, j);
                printf("%d\n", dis[i][j]);
                int k = j;
                while(i != k)
                {
                    printf("%d<---", k);
                    k = path[i][k];
                }
                printf("%d\n", i);
            }
        }

    }


}
int main()
{
    int u,v, w;
    scanf("%d%d%d", &n, &m, &ori);
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
    {
        if(i != j)
            dis[i][j] = INF;
        else
            dis[i][j] = 0;
    }
    while(m--)
    {
        scanf("%d%d%d", &u, &v, &w);
        dis[u][v] = w;
    }
    Floyd();
    prin_path();
    return 0;
}

SPFA算法:单源最短路径(可带负权重的环)

假设存在G=,dis[i]记录V0到i的最短距离,pre[i]记录从V0到i路径上i的前面的一个顶点;

step1.将所有顶点对之间距离初始化为无穷大(dis[i][j]=无穷大),pre[i]=i,vis[i]=0,将源点入队;

step2.读取队头顶点now,并将队头顶点now出队(记得消除标记),将与点now相连的所有点next进行松弛操作,更新dis[next],另外,如果点next没有在队列中,那么要将点next入队(记得标记),如果已经在队列中了,那么就不用入队(如果某个顶点入队超过V次,则说明图中有负环,直接跳出);

step3.重复step2,直到队空为止就完成了单源最短路的求解。


#include 
#include 
#include 
#include 
#include 

using namespace std;
#define MAXN 10000
const int INF = 0x3f3f3f3f;

struct Edge
{
    int v;
    int w;
    Edge(){}
    Edge(int vv, int ww):v(vv), w(ww){}
};

int dis[MAXN], vis[MAXN], pre[MAXN];
int cnt[MAXN];
int n, m, ori;
vector edge[MAXN];

int SPFA()
{
    queue q;
    for(int i = 1; i <= n; i++)
    {
        dis[i] = INF;
        vis[i] = 0;
        cnt[i] = 0;
        pre[i] = ori;
    }
    dis[ori] = 0;
    q.push(ori);
    vis[ori]=1;
    cnt[ori]++;
    while(!q.empty())
    {
        int now = q.front();
        q.pop();
        vis[now] = 0;
        for(int i = 0; i < edge[now].size(); i++)
        {
            int next = edge[now][i].v;
            if(dis[next] > dis[now] + edge[now][i].w)
            {
                dis[next] = dis[now] + edge[now][i].w;
                pre[next] = now;
                if(!vis[next])
                {
                    q.push(next);
                    vis[next] = 1;
                    cnt[next]++;
                    if(cnt[next] > n)
                        return 0;
                }

            }
        }

    }
    return 1;

}

void print_path(int pos)
{
    printf("%d to %d: ", ori, pos);
    printf("%d\n", dis[pos]);
    while(ori != pos)
    {
        printf("%d<---", pos);
        pos = pre[pos];
    }
    printf("%d\n", ori);
}

int main()
{
    int u, v, w;
    scanf("%d%d%d", &n, &m, &ori);
    while(m--)
    {
        scanf("%d%d%d", &u, &v, &w);
        edge[u].push_back(Edge(v, w));
    }
    if(SPFA())
    {
        for(int i = 1; i <= n; i++)
            if(dis[i] != INF)
                print_path(i);
            else
                printf("Have no path\n");
    }
    else
        printf("This map has negative circle\n");
    return 0;
}



Dijkstra算法参考: http://www.cnblogs.com/dolphin0520/archive/2011/08/26/2155202.html


Bellman-Ford算法参考:http://blog.csdn.net/niushuai666/article/details/6791765


Folyd算法参考:http://developer.51cto.com/art/201403/433874.htm


SPFA算法参考:http://www.cnblogs.com/scau20110726/archive/2012/11/18/2776124.html


你可能感兴趣的:(数据结构,算法分析)