。。。

导航

【图论】【最短路径模板+邻接表】【Floyed+Dijsktra+Bellman-Ford+SPFA】【最短路算法对比分析】

ACM模板

【最短路】:最短路是不包含回路的简单路径。

最短路径算法对比分析

【Floyed】

多源最短路,即要求求出图中每两个顶点之间的最短路。虽然Floyed的复杂度是O(n^3),但是4行却简单很多,本质上是动态规划算法。
思想:从i号顶点到j号顶点只经过前k号顶点的最短路径。

#define INF 999999

int Floyd()
{//初始化n个顶点 
    for(i = 1; i <= n; i ++)
        for(j = 1; j <= n; j ++)
            if(i == j)
                e[i][j] = INF;
            else
                e[i][j] = 0;
    for(k = 1; k <= n; k ++)//Floyd-Warshall算法核心语句 
        for(i = 1; i <= n; i ++)
            for(j = 1; j <= n; j ++)
                if(e[i][j] > e[i][k]+e[k][j])
                    e[i][j] = e[i][k]+e[k][j];
}

【Dijkstra】

Dijkstra算法适合不含负权边的单源最短路(单源最短路是指从源点到其余各个顶点的最短路径)。
思想:每次找到离源点最近的一个顶点,然后以该顶点为中心进行拓展,最终得到源点到其余所有点的最短路径

#define inf 99999999//用inf存储一个我们认为是正无穷的值
//读入n,m。n表示顶点个数,m表示边的条数 
memset(book,0,sizeof(book));
for(i = 1; i <= n; i ++)
    for(j = 1; j <= n; j ++)
        if(i == j)
            e[i][j] = 0;
        else
            e[i][j] = 1;

for(i = 1; i <= m; i ++)
{
    scanf("%d%d%d",&t1,&t2,&t3);
    e[t1][t2] = t3;
}
//初始化dis数组,1号顶点到其余各个顶点的初始路程 
for(i = 1; i <= n; i ++)
    dis[i] = e[1][i];
book[1] = 1;
//Dijkstra算法核心语句 
for(i = 1; i <= n-1; i ++)
{
    min = inf;
    //找到离1号顶点最近的顶点 
    for(j = 1; j <= n; j ++)
    {
        if(!book[j]&&dis[j]<min)
        {
            min = dis[j];
            u = j;  
        }   
    }   
}
book[u] = 1;
for(j = 1; j <= n; j ++)
{
    if(dis[j] > dis[u] + e[u][j])
        dis[j] = dis[u] + e[u][j];
}
for(i = 1; i <= n; i ++)//输出
    printf("%d ",dis[i]);
printf("\n");

【Bellman-Ford】

Bellman-Ford算法能解决存在负权边的单源点最短路径问题。可以检测一个图是否存在负权回路:如果在进行了n-1轮松弛后,仍然可以继续成功松弛,说明存在负权边。

#define inf 99999999
struct node{
    int from,to,w;
};
node edge[100];
bool Ford()
{
    for(i = 1; i <= n; i ++)
        dis[i] = inf;
    dis[1] = 0;
    for(i = 1; i <= n; i ++)
    {
        flag = false;
        for(j = 1; j <= m; j ++)
        {
            x = edge[i].from ;
            y = edge[i].to ;
            z = edge[i].w ;
            if(dis[y] > dis[x] + z)
            {
                dis[y] = dis[x] + z;
                flag = true;
            }
        }
        if(!flag)
            break;
        //如果更新到n遍,还能够继续更新dis数组,说明存在负权环 
        if(flag&&i == n) 
            return false;//返回false表示这个图存在负权环 
    }
    return true;//返回true表示求最短路成功 
}

【SPFA】

SPFA是Bellman-Ford算法的队列实现。SPFA加入了一个队列保存信息。用一个数组book来标记一个顶点是否已经加入了队列。
队列优化:只对最短路估计值发生了变化的顶点的所有出边执行松弛操作

void addedge(int a,int b,int c)
{
    e[num].to = b;
    e[num].w = c;
    e[num].next = first[a];//邻接表存图 
    first[a] = num++;
}//dis和fisrt在主函数中全部初始化为-1 
bool spfa(int s)
{
    int used[N];//用来记录一个顶点入队次数 
    bool book[N];//标记该点是否在队列中 
    int head,tail,i,to,now;
    queue<int>Q;
    memset(used,0,sizeof(used));
    memset(book,false,sizeof(book));
    Q.push(s); 
    book[s] = true;
    used[s]++;
    dis[s] = 0;
    while(!Q.empty())
    {
        now = Q.front() ;
        Q.pop() ;
        book[now] = false;
        for(i = first[now];i!=-1;i = e[i].next)
        {
            to = e[i].to ;
            if(dis[to]>dis[now]+e[i].w)
            {
                dis[to] = dis[now] + e[i].w ;
                used[to]++;
                if(used[to]>=n)//一个点使用超过n,一定存在负环 
                    return false;
                if(!book[to])//如果顶点不在队列中,入队 
                {
                    book[to] = true;//标记为已经入队 
                    Q.push(to) ;
                }
            }
        }
    } 
    return true;//不存在负权边 
}

【邻接表】

邻接表邻接矩阵时间复杂度和空间复杂度分析
假设图中有n个顶点,m条边
时间复杂度:邻接表直接存储边的信息,有向图是O(M),无向图是O(2*M)。邻接矩阵间接存储边的信息,复杂度是O(N*N)
空间复杂度:邻接表:O(N+M)或O(N+2*M),邻接矩阵:O(N*N)

//数组实现邻接表  共m条边

for(i = 1; i <= m; i ++)
{
    scanf("%d%d%d",&u[i],&v[i],&w[i]);
    next[i] = first[u[i]];
    first[u[i]] = i;
 } 

for(i = 1;i <= n; i ++)
{
    k = first[1];
    while(k!=-1)
    {
        printf("%d %d %d\n",u[k],v[k],w[k]);
        k = next[k];
    } 
}

posted on 2017-08-01 07:38  大学僧  阅读(513)  评论(0编辑  收藏  举报