C++有向帶權圖單源最短路徑

C++有向帶權圖單源最短路徑

參考《算法》最短路徑章節編寫實現代碼,包括無負權邊無環的Dijkstra算法,也包括可以處理一般性問題的Bellman-Ford算法。同時採用泛型編程,圖節點可以實現任意類型,具體原理參見《算法》最短路徑章節,實現代碼如下所示:

#include<iostream>
#include<algorithm>
#include<queue>
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<T> 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<T>::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<T>::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<T>::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<T>::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<T>::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<T>::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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章