C++有向带权图单源最短路径

C++有向带权图单源最短路径

参考《算法》最短路径章节编写实现代码,包括无负权边无环的Dijkstra算法,也包括可以处理一般性问题的Bellman-Ford算法。同时采用泛型编程,图节点可以实现任意类型,具体原理参见《算法》最短路径章节,实现代码如下所示:

#include
#include
#include
using namespace std;
/*
 顶点       中间节点
 VNode     ENode
 0 | A --> 2(C) 1(B)
 1 | B --> 4(E) 3(D) 0(A)
 2 | C --> 6(G) 5(F) 0(A)
 3 | D --> 7(H) 1(B)
 4 | E --> 7(H) 1(B)
 5 | F --> 6(G) 2(C)
 6 | G --> 5(F) 2(C)
 7 | H --> 4(E) 3(D)
 */

const int MAX = 20;
struct ENode                   //邻接表的中间节点
{
    int adjvex;                //对应索引
    double weight;             //边的权重
    ENode* next;
};

template<class T>
struct VNode                   //邻接表顶点
{
    T vertex;                  //值
    ENode* firstarc;           //指向第一个中间节点
};

template<class T>
class ALGraph                   //图
{
private:
    VNode adjList[MAX];      //邻接表数组
    int vexNum;                 //节点数量
    int arcNum;                 //连边数量
    bool visited[MAX];          //标记被访问
    int edgeTo[MAX];            //最短路径连边记录
    double distTo[MAX];         //最短距离
    queue<int> relax_edge;      //需要放松的队列
    bool onQ[MAX];              //标记是否在队列中
public:
    void CreateGraph();         //创建图
    void PrintGraph();          //打印图
    void Dijkstra(int source);
    void DijkstraVisit(int &delmin, int source);
    void BellmanFord(int source);  //BellmanFord算法
    void BellmanFord_Relax(int v); //放松操作
};

template<class T>
void ALGraph::CreateGraph()
{
    cout << "请输入图的顶点数:" << endl;
    cin >> this->vexNum;
    cout << "请输入图的弧数:" << endl;
    cin >> this->arcNum;
    cout << "请输入顶点信息:" << endl;
    for (int i = 0; i<this->vexNum; i++)  //构建顶点数组
    {
        cin >> this->adjList[i].vertex;
        this->adjList[i].firstarc = nullptr;
    }
    cout << "请输入" << this->arcNum << "个弧的信息:" << endl;
    for (int i = 0; i<this->arcNum; i++)  //构建每条邻接表
    {
        int h1, h2;
        double weight;
        cin >> h1 >> h2 >> weight;
        ENode* temp = new ENode();
        temp->adjvex = h2;
        temp->weight = weight;
        temp->next = this->adjList[h1].firstarc;
        this->adjList[h1].firstarc = temp;
    }
}

template<class T>
void ALGraph::PrintGraph()
{
    for (int i = 0; i<this->vexNum; i++)
    {
        cout << this->adjList[i].vertex << "--------->";
        ENode* p = this->adjList[i].firstarc;
        while (p)
        {
            cout << this->adjList[p->adjvex].vertex << "(" << p->weight << ")" << " ";
            p = p->next;
        }
        cout << endl;
    }
}

template<class T>
void ALGraph::Dijkstra(int source)
{
    int delmin = 0;                             //加入已知最短路径的点
    for (int i = 0; i < this->vexNum; i++)
    {
        visited[i] = false;
        distTo[i] = i == source ? 0 : __DBL_MAX__;  //初始化顶点到各点权重
    }
    for (int i = 0; i < this->vexNum; i++)
    {
        DijkstraVisit(delmin, source);          //加入已知点并与改点有关的所有权重
    }
    for (int i = 0; i < this->vexNum; i++)      //打印最短路径
    {
        cout << this->adjList[this->edgeTo[i]].vertex << "--->" << this->adjList[i].vertex << "  " << distTo[i] << endl;
    }
}

template<class T>
void ALGraph::DijkstraVisit(int& delmin, int source)
{
    double mindst = __DBL_MAX__;
    visited[delmin] = true;
    if (delmin == source)
    {
        edgeTo[source] = source;
        distTo[source] = 0;
    }
    ENode* p = this->adjList[delmin].firstarc;
    while (p)
    {
        if (visited[p->adjvex])
        {
            p = p->next;
            continue;
        }
        if (distTo[delmin] + p->weight < distTo[p->adjvex])  //如果经过delmin再到p点的距离小于原来起点到p点距离,需要更新权重
        {
            edgeTo[p->adjvex] = delmin;                      //更新经过的路径点
            distTo[p->adjvex] = distTo[delmin] + p->weight;  //更新距离
        }
        p = p->next;
    }
    for (int i = 0; i < this->vexNum; i++)            //找出下一次要加入已知最短路径部分的点
    {
        if (!visited[i] && mindst > distTo[i])
        {
            mindst = distTo[i];
            delmin = i;
        }
    }
}

template<class T>
void ALGraph::BellmanFord(int source)
{
    for(int i=0;i<this->vexNum;i++)          //初始化edgeTo[]和位于列表标记数组
    {
        distTo[i] = i==source? 0:__DBL_MAX__;
        onQ[i] = false;
    }
    relax_edge.push(source);       //源点加入放松队列
    onQ[source] = true;           //标记源点已存在于放松队列
    while(!relax_edge.empty())       //放松队列不为空,不断放松
    {
        int v = relax_edge.front();
        onQ[v] = false;
        BellmanFord_Relax(v);
        relax_edge.pop();
    }
    for (int i = 0; i < this->vexNum; i++)      //打印最短路径
    {
        cout << this->adjList[this->edgeTo[i]].vertex << "--->" << this->adjList[i].vertex << "  " << distTo[i] << endl;
    }
}

template<class T>
void ALGraph::BellmanFord_Relax(int v)
{
    ENode* p = this->adjList[v].firstarc;
    while(p)
    {
        if(distTo[p->adjvex]>distTo[v]+p->weight)     //需要放松
        {
            distTo[p->adjvex] = distTo[v]+p->weight;  //更新
            edgeTo[p->adjvex] = v;
            if(!onQ[p->adjvex])
            {
                relax_edge.push(p->adjvex);          //该点若不在队列,则将其加入队列
                onQ[p->adjvex] = true;
            }
        }
        p = p->next;
    }
}

int main()
{
    ALGraph<int> *graph = new ALGraph<int>();
    graph->CreateGraph();
    graph->PrintGraph();
    graph->BellmanFord(0);
    return 0;
}

你可能感兴趣的:(Algorithm)