浅谈【数据结构】图-最短路径问题

目录

1、最短路径问题

2、迪杰斯特拉算法

3、算法的步骤


谢谢帅气美丽且优秀的你看完我的文章还要点赞、收藏加关注

没错,说的就是你,不用再怀疑!!!

希望我的文章内容能对你有帮助,一起努力吧!!!


1、最短路径问题

最短路径问题:是指在图中找到两个顶点,求两个顶点之间最短路径的一个问题。

“最短”:通常来说是指路径上面总权值最小,权值(边/弧的长度、成本、时间...)。

最短路径问题计算机科学、运筹学、网络理论等多个领域都有广泛的应用。

在图中,路径是指:一个顶点到另一个顶点的方式。一个顶点到另一个顶点方式并不一定是唯一的,在 多个路径中找一个总权值最小的一个路径。

解决带权的有向图两个顶点之间最短路径问题

两个经典算法:

  • 迪杰斯特拉算法(Dijkstra)
  • 弗洛伊德算法(Floyd)

2、迪杰斯特拉算法

Dijkstra算法:是解决从网络中任意一个顶点(源点)出发,求它到其他顶点(终点)的最短路径的问题。

算法思路:按路径长度递增次序产生从某个源点v到图中其余各项顶点的最短路径

Dijkstra算法依赖两个辅助向量:

  • 向量S[n]
    • S[i]==1 (true),说明从源点v到终点vi的最短路径已经找到了。
    • S[i]==0 (false) , 说明从源点v到终点vi的最短路径还没有找到。
    • 初始化开始S[v]==1 (true) , 其余各项顶点均为0 (false)。
  • 向量dist[n]
    • dist[n]存放从源点v到终点vi这个顶点当前的最短路径
    • 初始化
      • 当v可以直接到到vi的时候,那么dist[vi] = 的权值w
      • 当v不可以直接到到vi的时候,那么dist[vi] = 无穷大

3、算法的步骤

很显然从源点v到其它各项顶点的最短路径中的第一短的路径,绝对是能直接到到达的顶点(邻接点)中 最短的一条路径

  • 第一步:
    • 从源点v到其它各项顶点的当前最短路径找出最短的来。
      • dist[u] = min{dist[w] (w=0,1,2,3,4,5....n-1),并且S[w]==0 (false))}
        • 最优路径=当前已知的最短路径中最短的一条
      • dist[u] 称为当前最优路径,u为当前最优的顶点下标
  • 第二步:
    • 用当前最优路径来更新其他的路径
      • 对S[w] == 0 的w(没有找到最优路径的顶点)进行更新。
      • 如果dist[u]+ 小于 dist[w] , 那么就更新dist[w]
  • 第三步:
    • 重复第一步和第二步

***迪杰斯特拉算法代码示例***

#include <iostream>

// 顶点数量是10个
#define VertexMaxCount 10

// 无穷大
#define MAXNUMBER (65535)

// 图类型
typedef struct 
{
    // 关系集
    int R[VertexMaxCount][VertexMaxCount];

    // 顶点集
    std::string V[VertexMaxCount];

    // 顶点数量
    int vertex_count;
}Graph;


// 关系类型
typedef  struct 
{
    int index_s; // 关系开始顶点下标
    int index_e; // 关系结束顶点下标
    int r;  // 关系
}R;

/*
    @brief 为一个邻接矩阵图增加一个顶点
    @param graph  需要增加顶点的图指针
    @param vertex 需要增加的顶点
*/
void addVertex(Graph *graph,std::string vertex)
{
    // 判断图是否存在
    if(!graph)
        return;
    
    // 添加新顶点
    graph->V[graph->vertex_count] = vertex;

    // 更新顶点数量
    graph->vertex_count++;
}

int getIndex(Graph*graph,std::string vertex)
{
    if(!graph)
        return -1;

    for(int index=0;index < VertexMaxCount;index++)
        if(graph->V[index] == vertex)
            return index; // 返回顶点在图中的下标

    return -1; // 表示顶点不在图中
}

/*
    @brief 为一个邻接矩阵图增加关系
    @param graph 需要增加关系的图指针
    @param r 增加新关系
*/
void addR(Graph *graph,R r)
{
    // 判断图是否存在
    if(!graph)
        return;
    
    // 添加关系
    graph->R[r.index_s][r.index_e] = r.r;
}

Graph *creatGraph()
{
    // 申请了一个邻接矩阵的空间
    Graph * graph = new Graph;

    std::cout << "请依次输入顶点:";
    // 增加顶点
    while(1)
    {
        std::string vertex = "结束";
        std::cin >> vertex;
        if(vertex == "结束")
            break; 

        // 增加进入图
        addVertex(graph,vertex);
    }

    // 先初始化关系
    for(int row = 0;row < VertexMaxCount;row++)
    {
        for(int column = 0;column < VertexMaxCount;column++)
            if(row == column)
                graph->R[row][column] = 0;
            else
                graph->R[row][column] = MAXNUMBER;
    }
    std::cout << "请输入顶点之间的关系:" << std::endl;
    // 增加关系
    while(1)
    {
        std::string start_vertex = "结束";
        std::string end_vertex = "结束";
        int ralation = MAXNUMBER;

        std::cin >> start_vertex;
        std::cin >> end_vertex;
        std::cin >> ralation;

        if(start_vertex == "结束"||end_vertex == "结束"||ralation == MAXNUMBER)
            break;

        R r;
        r.index_s = getIndex(graph,start_vertex);
        r.index_e = getIndex(graph,end_vertex);
        r.r = ralation;

        // 判断结点下班是否有效
        if(r.index_s == -1||r.index_e == -1)
            continue;

        // 存入关系
        addR(graph,r);
    }

    return graph;
}

/*
    @brief 打印一个邻接矩阵图
    @param graph 需要打印的邻接矩阵图指针
*/
void printGraph(Graph *graph)
{
    if(!graph)
        return ;
    
    std::cout << "\t";
    // 打印顶点
    for(int count=0;count < VertexMaxCount;count++)
        std::cout << graph->V[count] << "\t";
    std::cout << std::endl;

    // 打印关系
    for(int row = 0;row < graph->vertex_count;row++)
    {
        
        std::cout << graph->V[row] << "\t";
        for(int column = 0;column < VertexMaxCount;column++)
        {
            // 存在关系
            std::cout << graph->R[row][column] << "\t";
        }
        std::cout << std::endl;
    }
}

/*
    逻辑思路:
        算法的步骤
        很显然从源点v到其它各项顶点的最短路径中的第一短的路径,绝对是能直接到到达的顶点(邻接点)中最短的一条路径
        - 第一步:
        - 从源点v到其它各项顶点的当前最短路径找出最短的来。
            - dist[u] = min{dist[w] (w=0,1,2,3,4,5....n-1),并且S[w]==0 (false))}
            - 最优路径=当前已知的最短路径中最短的一条
            - dist[u] 称为**当前最优路径**,u为当前最优的顶点下标
        - 第二步:
        - 用当前最优路径来更新其他的路径
            - 对S[w] == 0 的w(没有找到最优路径的顶点)进行更新。
            - 如果dist[u]+<u,w> 小于 dist[w] , 那么就更新dist[w]
        - 第三步:
        - 重复第一步和第二步

    @brief 通过迪杰斯特拉算法求最短路径
    @param graph 需要进行最短路径查找的图
    @param v_src 源点
*/


// 两个辅助向量
static bool  S[VertexMaxCount];
static int dist[VertexMaxCount];

void Dijkstra(Graph *graph,std::string v_src)
{
//------------------------迪杰斯特拉准备工作---------------------------
    // 初始化向量S
    for(int count = 0;count < graph->vertex_count;count++)
        S[count] = false; // 把所有顶点初始化的时候置为没有找到最短路径

    // 将v_src 到 v_src设置为找到了最短路径:自己到自己的最优路径找到了
    int v_src_pos = getIndex(graph,v_src);
    S[v_src_pos] = true;

    // 初始化dist向量
    for(int count = 0;count < graph->vertex_count;count++)
        dist[count] = graph->R[v_src_pos][count]; // 权值存储到dist里面

    // 结束之后:dist存储了源点到其他顶点的路径权值

//------------------------正式开始迪杰斯特拉---------------------------
    int min_pos; // 用来存储当前最短路径的下标
    int min_w;   // 用来存储当前最短路径的权值

    for(int count = 0;count < graph->vertex_count-1;count++)
    {
        // 初始化当前最短路径的下标和权值
        min_pos = 0;
        min_w = MAXNUMBER;

        // 第一步:从源点v到其他各项顶点的当前最短路径中找出最短的路径
        for(int min_road_pos = 0;min_road_pos < graph->vertex_count;min_road_pos++)
        {
            // 最优路径=当前已知的最短路径中最短的一条
            if(S[min_road_pos]==false&&dist[min_road_pos] < min_w)
            {
                min_pos = min_road_pos; // 保存小的那个下标
                min_w   = dist[min_road_pos]; // 保存它的权值
            }
        }
        // 当循环结束,你就能拿到当前最短路径中的第一短

        std::cout << "当前最短路径为:" << graph->V[v_src_pos] << "->" << graph->V[min_pos] << ":" << min_w << std::endl;

        // 这一条路径是源点到min_pos顶点的最优路径了,标记它
        S[min_pos] = true;

        // 第二步:通过这个最短路径来更新其他顶点路径
        for(int pos = 0;pos < graph->vertex_count;pos++)
        {
            //  如果dist[min_pos]+<min_pos,pos> 小于 dist[pos] , 那么就更新dist[pos]
            if(S[pos] == false&&dist[min_pos]+graph->R[min_pos][pos] < dist[pos])
            {
                // 更新pos对应的顶点的路径
                dist[pos] = dist[min_pos] + graph->R[min_pos][pos];
            }
        }
    }
    // 整个循环结束之后,从v_src到其他各项顶点的最短路径就求出来了

    for(int count = 0; count < graph->vertex_count;count ++)
    {
        std::cout << graph->V[v_src_pos] << "->" << graph->V[count] << ":" << dist[count] << std::endl;
    }
}


int main()
{
    Graph *g = creatGraph();

    printGraph(g);

    Dijkstra(g,"a");

    delete g;
    
    return 0;
}
  • 14
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值