1. 程式人生 > >資料結構與演算法——最短路徑Dijkstra演算法的C++實現

資料結構與演算法——最短路徑Dijkstra演算法的C++實現

#ifndef GRAPH_H
#define GRAPH_H

#include <list>
#include <iostream>
#include <vector>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <iterator>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <queue>

using namespace std;

#define MAX_VERTEX_NUM 600
#define INFINITY 1000000//將INFINITY定義為無窮大的值

//儲存每個頂點資訊的資料結構
struct GraphNode{
    bool known;//當前頂點距離起點的距離是否確定
    int dist;//當前頂點到起點的最短距離
    int path;//當前頂點距離起點的最短路徑的前一個頂點
};

//圖節點資訊
typedef struct Node{ 
    int edge_num;//邊號 
    int src;//源點 
    int vertex;//自身 
    int weight;//邊的權重 
}Node; 

/*******************************************************
*  類名稱: 鄰接表圖
********************************************************/ 
class Graph{
    private:
        int edge_num;//圖邊的個數
        int vertex_num;//圖的頂點數目
        list<Node> * graph_list;//鄰接表
        vector<GraphNode> nodeArr;//儲存每個頂點資訊的陣列
        
    public:
        Graph(){}
        Graph(char* graph[], int edgenum); 
        ~Graph();
        void print();
        void dijkstra(int src);
        void printShorestPath(); 
    private:
        vector<int> get_graph_value(char* graph[], int columns);
        void addEdge(char* graph[], int columns);
};


/*************************************************
*  函式名稱:dijkstra(int src)
*  功能描述:求無權圖的任意點到其它頂點的距離
*  引數列表:src是起點
*  返回結果:void 
*************************************************/
void Graph::dijkstra(int src)
{
    //初始化頂點資訊
    for(int i = 0; i < vertex_num; ++i){
        nodeArr[i].known = false;
        nodeArr[i].dist = INFINITY;
        nodeArr[i].path = 0;
    }
    //重要的一步,開啟演算法的關鍵一步
    nodeArr[src].dist = 0;

    for(; ;){
        //找到unknown的dist最小的頂點 
        int v = 0;
        int max = INFINITY;
        for(int i = 0; i < vertex_num; ++i){
            if(!nodeArr[i].known && (max > nodeArr[i].dist)){
                max = nodeArr[i].dist;
                v = i;
            }
        }

        //沒有找到滿足條件的頂點,退出演算法
        if(max == INFINITY)
            break;

        nodeArr[v].known = true;
        //更新與v相鄰所有頂點w的dist,path
        for(list<Node>::iterator it = graph_list[v].begin(); it != graph_list[v].end(); ++it){
            if(!nodeArr[(*it).vertex].known){
                if(nodeArr[v].dist + (*it).weight < nodeArr[(*it).vertex].dist){
                    nodeArr[(*it).vertex].dist = nodeArr[v].dist + (*it).weight;
                    nodeArr[(*it).vertex].path = v;
                }
            }
        }

    }
}

/*************************************************
*  函式名稱:printShorestPath()
*  功能描述:將獲得的src頂點到其它頂點的最短路徑輸出
*  引數列表:無
*  返回結果:無
*************************************************/
void Graph::printShorestPath()
{
    cout << "頂點\t" << "known\t" << "dist\t" << "path" << endl;
    for(int i = 0; i < vertex_num; ++i){
        if(nodeArr[i].known)
            cout << i << "\t" << nodeArr[i].known << "\t" << nodeArr[i].dist << "\t" << nodeArr[i].path << endl;
    } 
}

/*************************************************
*  函式名稱:print
*  功能描述:將圖的資訊以鄰接表的形式輸出到標準輸出
*  引數列表:無
*  返回結果:無
*************************************************/
void Graph::print()
{
    cout << "******************************************************************" << endl; 
    //for(int i = 0 ; i < MAX_VERTEX_NUM; ++i){
    for(int i = 0 ; i < vertex_num; ++i){
        if(graph_list[i].begin() != graph_list[i].end()){
            cout << i << "-->";
            for(list<Node>::iterator it = graph_list[i].begin(); it != graph_list[i].end(); ++it){
                cout << (*it).vertex << "(邊號:" << (*it).edge_num << ",權重:" << (*it).weight << ")-->";
            }
            cout << "NULL" << endl;
        }
    }

    cout << "******************************************************************" << endl; 
}

/*************************************************
*  函式名稱:get_graph_value
*  功能描述:將圖的每一條邊的資訊儲存到一個數組中
*  引數列表: graph:指向圖資訊的二維陣列
             columns:圖的第幾條邊
*  返回結果:無
*************************************************/
vector<int> Graph::get_graph_value(char* graph[], int columns)
{
    vector<int> v;
    char buff[20];
    int i = 0, j = 0, val;
    memset(buff, 0, 20);

    while((graph[columns][i] != '\n') && (graph[columns][i] != '\0')){
        if(graph[columns][i] != ','){
            buff[j] = graph[columns][i];
            j++;
        }
        else{
            j = 0;
            val = atoi(buff); 
            v.push_back(val);
            memset(buff, 0, 20);
        }
        i++;
    }
    val = atoi(buff); 
    v.push_back(val);

    return v;
}



/*************************************************
*  函式名稱:addEdge
*  功能描述:將圖的每一條邊的資訊加入圖的鄰接表中
*  引數列表:graph:指向圖資訊的二維陣列
             columns:圖的第幾條邊
*  返回結果:無
*************************************************/
void Graph::addEdge(char* graph[], int columns)
{
    Node node;
    vector<int> v = get_graph_value(graph, columns);

    node.edge_num = v[0];
    node.src = v[1];
    node.vertex = v[2];
    node.weight = v[3];


    //根據頂點的標號,求的總的頂點數目
    if(node.vertex > vertex_num)
        vertex_num = node.vertex;

    //要考慮重複的邊,但是邊的權重不一樣
    for(list<Node>::iterator it = graph_list[node.src].begin(); it != graph_list[node.src].end(); ++it){
        if((*it).vertex == node.vertex){
            if((*it).weight > node.weight){
                (*it).weight = node.weight;   
            }
            return;
        }
    }

    graph_list[node.src].push_back(node);
}


/*************************************************
*  函式名稱:建構函式
*  功能描述:以鄰接表的形式儲存圖的資訊,並儲存必須經過的頂點
*  引數列表:graph:指向圖資訊的二維陣列
             edgenum:圖的邊的個數
*  返回結果:無
*************************************************/
Graph::Graph(char* graph[], int edgenum):nodeArr(MAX_VERTEX_NUM)
{
    edge_num =  edgenum; 
    vertex_num = 0;
    graph_list = new list<Node>[MAX_VERTEX_NUM+1];


    for(int i = 0; i < edgenum; ++i){
        addEdge(graph, i);   
    }

    //對頂點資訊進行初始化
    for(int i = 0; i < MAX_VERTEX_NUM; ++i){
        nodeArr[i].known = false;
        nodeArr[i].dist = INFINITY;
        nodeArr[i].path = -1;//如果為-1,表示沒有該點,配合dijkstra演算法使用
    }

    vertex_num++;
}


/*************************************************
*  函式名稱:解構函式
*  功能描述:釋放動態分配的記憶體
*  引數列表:無
*  返回結果:無
*************************************************/
Graph::~Graph()
{
    delete[] graph_list;
}

#endif