代码改变世界

贪心 单源最短路径

  youxin  阅读(800)  评论(0编辑  收藏  举报

 最短路径问题是图论研究中的一个经典算法问题, 旨在寻找图(由结点和路径组成的)中两结点之间的最短路径。 算法具体的形式包括:

     确定起点的最短路径问题 - 即已知起始结点,求最短路径的问题。适合使用Dijkstra算法

  确定终点的最短路径问题 - 与确定起点的问题相反,该问题是已知终结结点,求最短路径的问题。在无向图中该问题与确定起点的问题完全等同,在有向图中该问题等同于把所有路径方向反转的确定起点的问题。

  确定起点终点的最短路径问题 - 即已知起点和终点,求两结点之间的最短路径

  全局最短路径问题 - 求图中所有的最短路径。Floyd-Warshall算法
 
这个算法是通过为每个顶点 v 保留目前为止所找到的从s到v的最短路径来工作的。初始时,原点 s 的路径长度值被赋为 0 (d[s] = 0),同时把所有其他顶点的路径长度设为无穷大,即表示我们不知道任何通向这些顶点的路径(对于 V 中所有顶点 v 除 s 外d[v] = ∞)。当算法结束时,d[v] 中储存的便是从 s 到 v 的最短路径,或者如果路径不存在的话是无穷大。 Dijkstra 算法的基础操作是边的拓展:如果存在一条从 u 到 v 的边,那么从 s 到 v 的最短路径可以通过将边(u, v)添加到尾部来拓展一条从 s 到 u 的路径。这条路径的长度是 d[u] + w(u, v)。如果这个值比目前已知的 d[v] 的值要小,我们可以用新值来替代当前 d[v] 中的值。拓展边的操作一直执行到所有的 d[v] 都代表从 s 到 v 最短路的花费。这个算法经过组织因而当 d[u] 达到它最终的值的时候每条边(u, v)都只被拓展一次。
算法维护两个顶点集 S 和 Q。集合 S 保留了我们已知的所有 d[v] 的值已经是最短路径的值顶点,而集合 Q 则保留其他所有顶点。集合S初始状态为空,而后每一步都有一个顶点从 Q 移动到 S。这个被选择的顶点是 Q 中拥有最小的 d[u] 值的顶点。当一个顶点 u 从 Q 中转移到了 S 中,算法对每条外接边 (u, v) 进行拓展。
http://baike.baidu.com/view/4719095.htm
 
dijkstra算法思想:
开始时,S={u},T=V-{u}; 对T中所有顶点x,如果u到x存在边,置d(u,x)=c(u,x); 否则d(u,x)=无穷大。然后,对T中所有顶点x,
寻找d(u,x)最小的顶点t,即:
 d(u,t)=min(d(u,x)|x属于T)
则d(u,t)就是顶点t到顶点u的最短路径距离。同时,顶点t也是集合T中所有顶点距离u最近的顶点。把顶点t从T中删除,把它并入S。然后对T中与t相邻接的所有顶点x,用下面的公式更新d(u,x)的值:
 d(u,x)=min(d(u,x), d(u,t) +c(t,x);
继续上面的步骤,一直到T空为止。
类似代码实现:
复制代码
#include<iostream>
using std::iostream;  //不能用using namepace std;应为utility中有一个函数 prev,在传递参数prev,编译错误不明确
using std::cout;
using std::cin;
using std::endl;

const int maxnum = 100;
const int maxint = 999999;
 
//各数组都从下标1开始
int dist[maxnum]; //表示当前点到原点的最短路径长度
int prev[maxnum]; //记录当前点的前一个结点
int c[maxnum][maxnum]; //记录图的两点间路径长度
int n,line; //图的结点数和路径数
// n -- n nodes
// v -- the source node
// dist[] -- the distance from the ith node to the source node
// prev[] -- the previous node of the ith node
// c[][] -- every two nodes' distance
                       
void Dijstra(int n,int v,int *dist,int *prev,int c[maxnum][maxnum])
{
    bool s[maxnum]; //是否应该存到S集合中
    for(int i=1;i<=n;i++)
    {
        dist[i]=c[v][i];
        s[i]=0; //初始都未用过该点
        if(dist[i]==maxint)
            prev[i]=0;
        else
            prev[i]=v;
    }
    dist[v]=0;
    s[v]=1;
    // 依次将未放入S集合的结点中,取dist[]最小值的结点,放入结合S中
    // 一旦S包含了所有V中顶点,dist就记录了从源点到所有其他顶点之间的最短路径长度
    // 注意是从第二个节点开始,第一个为源点
    for(int i=2;i<=n;i++)
    {
        int tmp=maxint;
        int u=v; 
        //找出当前未使用的点J 的dist[j]的最小值
        for(int j=1;j<=n;j++)
            if(!s[j]&&dist[j]<tmp)
            {
                u=j;   // u保存当前邻接点中距离最小的点的号码
                tmp=dist[j];
            }
        s[u]=1; //表示点u已存入S集合中
        //跟新dist
        for(int j=1;j<=n;j++)
            if((!s[j])&&c[u][j]<maxint)
            {
                int newdist=dist[u]+c[u][j];
                if(newdist<dist[j])
                {
                    dist[j]=newdist;
                    prev[j]=u;
                }
            }
    }

}


//查找从原点v到终点u的路径,并输出
void  searchPath(int *prev,int v,int u)
{
    int que[maxnum];
    int tot=1;
    que[tot]=u;
    tot++;
    int tmp=prev[u];
    while(tmp!=v)
    {
        que[tot]=tmp;
        tot++;
        tmp=prev[tmp];
    }
    que[tot]=v;
    for(int i=tot;i>=1;--i)
        if(i!=1)
            cout<<que[i]<<"->";
        else
            cout<<que[i]<<endl;
}


 


int main()
{
    freopen("dijkstra.txt","r",stdin);
     

    //各数组下标都从1开始
    //输入结点数,
    cin>>n;
    //输入路径数
    cin>>line; 
    int p,q,len; //输入p ,q两点及其路径长度
    //初始化c[][]为maxint
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            c[i][j]=maxint;
    for(int i=1;i<=line;++i)
    {
        cin>>p>>q>>len;
        if(len<c[p][q]) //有重边
        {
            c[p][q]=len; //p指向q
            c[q][p]=len; //q指向p,这样表示无向图
        }
    }
    for(int i=1;i<=n;++i)
        dist[i]=maxint;
    /*
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=n;j++)
            printf("%8d",c[i][j]);
        printf("\n");
    }
    */
    Dijstra(n,1,dist,prev,c);

    //最短路径长度
    /*
    cout<<"源点到最后一个顶点的最短路径长度: "<<dist[n]<<endl;
    //路径
    cout<<"源点到最后一个顶点的路径为: "<<endl;
    */
    cout<<"顶点a到个顶点的最短路径为"<<endl;
    for(int i=2;i<=n;i++)
    {
        searchPath(prev,1,i);
    }
    /*
    searchPath(prev,1,2);
    searchPath(prev,1,3);
    searchPath(prev,1,n);
    */
}
   
复制代码

参考:http://www.wutianqi.com/?p=1890

 

 

 

 

 

复制代码
#include<iostream>
#include<sstream>

using namespace std;

#define N 500
#define INF INT_MAX



typedef struct Graph {
    int vexnum, arcnum;//点数和边数
    int arcs[N][N]; //邻接矩阵
    string vexs[N];//顶点表

}Graph;
stringstream ss;

int dist[N];
int preV[N]; //path通过保存路径的前驱结点来保存路径




void Dijkstra(Graph& G,int v0)
{
    bool s[N];//是否应该存到S集合中
    for (int i = 1; i <= G.vexnum; i++)
    {
        dist[i] = G.arcs[v0][i];
        s[i] = 0;//初始都没有使用过
        preV[i] = dist[i] < INF ? v0 : -1;


    }
    dist[v0] = 0;
    s[v0] = 1;

    for (int i = 2; i <= G.vexnum; i++)
    {
        int tmp = INF;
        int u = v0;
        ////找出当前未使用的点J 的dist[j]的最小值
        for (int j = 1; j <= G.vexnum; j++)
        {
            if (!s[j] && dist[j] < tmp) {
                u = j; ////u保存当前邻接点中距离最小的点的号码
                tmp = dist[j];
            }
        }

        s[u] = 1;//表示点u 已经存入S集合中

        //更新dis
        for (int j = 1; j <= G.vexnum; j++)
        {
            if (!s[j] && G.arcs[u][j] < INF)
            {
                int newdist = dist[u] + G.arcs[u][j];
                if (newdist < dist[j])
                {
                    dist[j] = newdist;
                    preV[j] = u;

                }
            }
        }


    }

}
//查找从原点v到终点u的路径,并输出
void  searchPath( int v, int u)
{
    int que[N];
    int tot = 1;
    que[tot] = u;
    tot++;
    int tmp = preV[u];
    while (tmp != v)
    {
        que[tot] = tmp;
        tot++;
        tmp = preV[tmp];
    }
    que[tot] = v;
    for (int i = tot; i >= 1; --i)
        if (i != 1)
            cout << que[i] << "->";
        else
            cout << que[i] << endl;
}
void printDist(Graph& G)
{
    for (int i = 1; i <= G.vexnum; i++)
    {
        cout << "顶点:"<< i << " dist "<< dist[i] << " ";
        cout << endl;
    }
    cout << endl;
}

void createGraph(Graph& G) {
    ss >> G.vexnum >> G.arcnum;
    for (int i = 1; i <=G.vexnum; i++)
    {
        for (int j = 1; j <=G.vexnum; j++)
        {
            G.arcs[i][j] = INF;
        }
    }
    for (int i = 1; i <= G.arcnum; i++)
    {
        int u, v, w;
        ss >> u >> v >> w;
        G.arcs[u][v] = w;

    }
}


int main()
{
    string raw_str = R"(  
            5  7

         1 2  10 
         1 4 30 
         1 5 100 

         2 3 50 
         3  5 10 
        
         4 3 20 
         4 5 60 
         

    )";
    ss << raw_str;
    Graph G;
    createGraph(G);

    Dijkstra(G, 1);

    searchPath( 1, 5);
    cout << endl;

    printDist(G);
}
复制代码

 

编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
历史上的今天:
2012-08-16 php开发简单网站模板
点击右上角即可分享
微信分享提示