迪杰斯特拉算法

这是一个经典的求图的最短路径算法。


我发现不同的教材上面所采用的不同的描述方式实在都有点费解,所以这里决定自己给出一种描述方式:

主要思想是将顶点划分为两个集合:已找到最短路径的加入S集,尚未找到的在U集。

(1)创建一个数组dist[size],记录所有顶点到源点的当前已知最短距离,初始均为无穷大。

(2)选取源点,加入S集,源点到源点的距离为0。

(3)更新新加入S集的点new_vertex所有可达的点v[i]到源点的最短距离,具体方式是比较distance(v[i], new_vertex)+dist[new_vertex]的值和dist[v[i]]的值,谁小就取谁。

(4)在所有未加入S集的点中,取dist最小的一个,加入S。

(5)重复(3)、(4)直至所有存在路径的点都已加入S。

(不存在路径的点,dist会一直为无穷大,最后是否算加入S,可灵活处理,比如我这里的代码,干脆就不加入S了。)


代码如下:

根据自己的理解可能和各种教材上的有些不一样。

用了较多stl容器,效率可能较低。

一般邻接表法表示图,弧都用链表来记录,但我觉得vector也行。

因为顶点只记录了所有从此点出发的弧,所以导致算法中一些计算很不方便,可以考虑在顶点结构中直接加入各弧头顶点的索引,以后用起来就比较方便些。

因为在创建图时,用的是vector的push_back方法,所以必须按顺序输入各顶点,不支持乱序输入。


/*
迪杰斯特拉算法
部分函数定义为inline形式纯粹为书写方便
*/
#include <iostream>
#include <vector>
#include <stack>

using std::cin;
using std::cout;
using std::endl;
using std::vector;
using std::stack;

struct CArc
{
    int m_iFrom;
    int m_iTo;

    double m_dDistance;

public:
    inline CArc(int from, int to, int dis):
                        m_iFrom(from),
                        m_iTo(to),
                        m_dDistance(dis)
                        {}
    inline int GetTo(){return m_iTo;}
    inline double GetDistance(){return m_dDistance;}
};

class CVertex
{
    int m_iIndex;
    vector<CArc> m_Arcs;

public:
    inline CVertex(int index):m_iIndex(index){}
    inline void AddArc(CArc arc)
    {
        m_Arcs.push_back(arc);
    }
    inline vector<CArc> GetArcs()
    {
        return m_Arcs;
    }

    int m_iFront;   //专为Dijkstra算法保存已得路径设置的前一个节点索引
};

class CWDiGraph
{
    vector<CVertex> m_Vertexes;
    
public:
    void AddVertex(int m_iIndex);
    void AddArc(int m_iFrom, int m_iTo, double m_dDistance);

    void ShowMatrix();  //邻接矩阵形式显示图
    void ShowList();    //邻接表形式显示图

    void Dijkstra(int m_iFrom, int m_iTo);  //采用迪杰斯特拉算法搜寻最短路径并输出
    void ShowShortestPath(int m_iFrom, int m_iTo);
};

//注意必须按顺序添加每个顶点,不支持乱序输入
void CWDiGraph::AddVertex(int m_iIndex)
{
    if (m_iIndex != m_Vertexes.size())
    {
        cout<<"input error, please import vertexes in correct order"<<endl;
        getchar();
        return;
    }

    CVertex vertex(m_iIndex);
    m_Vertexes.push_back(vertex);
}

void CWDiGraph::AddArc(int m_iFrom, int m_iTo, double m_dDistance)
{
    if ((m_iFrom < 0) || (m_iFrom > m_Vertexes.size())
        || (m_iTo < 0) || (m_iTo > m_Vertexes.size()))
    {
        cout<<"input error, please check the parameter"<<endl;
        getchar();
        return;
    }

    CArc arc(m_iFrom, m_iTo, m_dDistance);
    m_Vertexes[m_iFrom].AddArc(arc);
}

void CWDiGraph::ShowMatrix()
{
    for(int i=0; i<m_Vertexes.size(); ++i)
    {
        double *row = new double[m_Vertexes.size()];
        for(int j=0; j<m_Vertexes.size(); ++j)
        {
            row[j] = DBL_MAX;   //所有顶点距离预置为INFINITE
        }

        //记录可达顶点的距离
        for(int j=0; j<m_Vertexes[i].GetArcs().size(); j++)
        {
            int to = (m_Vertexes[i].GetArcs())[j].GetTo();
            row[to] = m_Vertexes[i].GetArcs()[j].GetDistance();
        }

        //output
        for(int j=0; j<m_Vertexes.size(); ++j)
        {
            if (row[j] == DBL_MAX)
            {
                cout<<"× "; //无穷大距离用叉号表示
            }
            else
            {
                cout<<row[j]<<" ";
            }  
        }

        cout<<endl;

        delete row;
        row = NULL;
    }
}

void CWDiGraph::ShowList()
{
    //略
}

void CWDiGraph::Dijkstra(int m_iFrom, int m_iTo)
{
    int size = m_Vertexes.size();
    bool *found = new bool[size];       //用于记录顶点是否已加入S集合
    double *dist = new double[size];    //当前最短路径距离列表
    
    int new_vertex;                     //新加入S的点

    for(int i=0; i<size; ++i)
    {
        found[i] = 0;
        dist[i] = DBL_MAX;
    }

    found[m_iFrom] = true;
    dist[m_iFrom] = 0;          //源点到自身距离为0
    new_vertex = m_iFrom;
    
    for(int i=1; i<size; ++i)   //注意此处不能用while (!found[m_iTo]),因为可能有些点没有路径可达,导致死循环
    {
        //更新与新加入顶点相连的顶点的距离值
        vector<CArc> arcs = m_Vertexes[new_vertex].GetArcs();
        for (int i=0; i<arcs.size(); ++i)
        {
            //记录顶点的最短路径父节点
            m_Vertexes[arcs[i].GetTo()].m_iFront = new_vertex;

            //更新最小距离值
            if ((arcs[i].GetDistance() + dist[new_vertex]) < dist[arcs[i].GetTo()])
            {
                dist[arcs[i].GetTo()] = arcs[i].GetDistance() + dist[new_vertex];
            }     
        }
        
        //找出尚未加入S的距离值最小的顶点,加入S
        double min_dist = DBL_MAX;          //最短距离
        for(int i=0; i<size; ++i)
        {
            if ((!found[i]) && (dist[i] < min_dist))
            {
                min_dist = dist[i];
                new_vertex = i;
            }
        }

        found[new_vertex] = true;       
    }

    //Show result
    if (found[m_iTo])
    {
        ShowShortestPath(m_iFrom, m_iTo);
    }
    else
    {
        cout<<"there's no way to vertex "<<m_iTo<<endl;
    }
}

void CWDiGraph::ShowShortestPath(int m_iFrom, int m_iTo)
{
    
    stack<int> path;
    int cur_vertex = m_iTo;

    do
    {
        path.push(cur_vertex);
        cur_vertex = m_Vertexes[cur_vertex].m_iFront;
    }   
    while (cur_vertex != m_iFrom);
    
    cout<<cur_vertex;
    while (!path.empty())
    {      
        cur_vertex = path.top();
        path.pop();
        cout<<"--"<<cur_vertex;
    }
    cout<<endl;
}

int main()
{
    CWDiGraph graph;

    //输入见《数据结构》图7.34
    for(int i=0; i<6; ++i)
    {
        graph.AddVertex(i);
    }
    graph.AddArc(0, 5, 100);
    graph.AddArc(0, 4, 30);
    graph.AddArc(0, 2, 10);
    graph.AddArc(1, 2, 5);
    graph.AddArc(2, 3, 50);
    graph.AddArc(3, 5, 10);
    graph.AddArc(4, 3, 20);
    graph.AddArc(4, 5, 60);

    graph.ShowMatrix();

    for(int i=1; i<6; ++i)
    {
        graph.Dijkstra(0,i);
    }   
}

/*
运行结果:
× × 10 × 30 100
× × 5 × × ×
× × × 50 × ×
× × × × × 10
× × × 20 × 60
× × × × × ×
there's no way to vertex 1
0--2
0--4--3
0--4
0--4--3--5

注意点:
注意搜索以及显示最短路径时,都需考虑没有路径的情况
*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值