图的路径问题

图的路径

路径问题

在图的相关问题当中,我们经常会遇到路径问题,即在图中某两个节点之间的距离.
这个距离也许并不仅仅是相隔的节点数,也有可能代表一些其他的值,比如距离,时间.

此时广度优先搜索(BFS)是一种不错的选择.广度优先搜索按照层次逐层处理节点,这为我们计算顶点到某一节点的距离提供了便利.
我给出一个简单的实现方案:

class BFSTest {
public:
    map<string, vector<string>> pointList;
    map<string, bool> pointVisitted;

    //有向图
    void initPointList(vector<pair<string,string>> path) {
        pointList.clear();
        for (size_t i = 0; i < path.size(); i++) {
            if(pointList[path[i].first] == pointList.end()){
                vector<string> temp;
                pointList[path[i].first] = temp;
            }
            pointList[path[i].first].push_back(path[i].second);
        }
    }

    void initVisitted() {
        for(auto it = pointVisitted.begin(); it != pointVisitted.end(); it++) {
            it->second = false;
        }
    }

    int graphBFS(string begin, string end) {
        queue<pair<string,int>> findQueue;
        pair<string,int> node;
        findQueue.push(pair<string,int>(begin,0));

        while(!findQueue.empty()) {
            node = findQueue.front();
            findQueue.pop();
            pointVisitted[node.first] = true;
            for (size_t i = 0; i < pointList[node.first].size(); i++) {
                if(pointVisitted[pointList[node.first][i]] == false){
                    if(pointList[node.first][i] == end) {
                        return node.second + 1;
                    }
                    findQueue.push(pair<string,int>(pointList[node.first][i],node.second+1));
                }
            }
        }

        return -1;
    }

    int calcDistance(vector<pair<string,string>> path, vector<pair<string, string>> queries) {
        vector<int> results;
        initPointList(path);
        for (size_t i = 0; i < queries.size(); i++) {
            results.push_back(graphBFS(queries[i].first,queries[i].second));
        }
    }
}

Dijkstra算法

  • 在实际应用中,我们遇到的问题可能比上面提到的要复杂的多.最直观的一个例子,节点之间的'距离'可能并不是我们在上文代码中实现的单位长度(也就是1).此时我们拥有的则是一个加权图.那么这种图的最短路径问题应该怎么解决呢?
  • 让我们来想象这样一种情况,我们首先给出一道题.小明从A地出发,要去到D地.A到D之间的路径关系如下:
    A->B = 5
    A->C = 10
    B->D = 5
    C->D = 1

我们应该如何站在计算机的角度去考虑这个问题?闹钟算法是这么解决的,小米和朋友同时从A出发,去往可达的B,C.当小明到达B时,小明的朋友距离C还有5个单位距离.此时小明已经发现了D并向D走去.过了5个单位时间后小明已到达终点D,此时小明的朋友到达C点.

  • 如果我们将题目中的距离关系改成这样呢?
    A->B = 5
    A->C = 8
    B->D = 10
    C->D = 2

按照刚才的思路,小明在从A到达B的时候,小明的朋友距离C还有3个单位距离.此时小明从B出发去往D.在3个单位距离的时间后,小明的朋友到达C,此时他从C出发去往D.在2个单位距离后小明的朋友到达D.此时小明距离D还有5个单位距离.

  • 让我们概括一下刚才的思路:我们以时间为单位,在相同时间内我们沿每条路径移动相同的距离.每次到达新的节点的时候,我们都会停下来更新整个路径图.已经在走的路径长度被更新为剩余路径的长度,而新发现的路径则被加入到路径图中去.直到某一条路径到达终点为止.
    代码实现:

bool compare(const cityAndDistance a,const cityAndDistance b) {
    return a->second < b->second;
}

class shortestPath{
public:
    typedef pair<string, int> cityAndDistance;
    map<string, vector<cityAndDistance>> graph;
    map<string, cityAndDistance> prev;
    map<string, bool> visited;

    void initGraph(vector<pair<string, pair<string,int>>> path) {
        for (size_t i = 0; i < path.size(); i++) {
            if(graph.find(path[i].first) != graph.end()) {
                vector<cityAndDistance> temp;
                graph[path[i].first] = temp;
            }
            graph[path[i].first].push_back(cityAndDistance(path[i].second));
        }
    }

    //基于闹钟算法,每次到达一个新的节点就将已发现的节点距离更新.
    void update(map<string,int>& queuePri) {
        visited[first.first] = true;
        auto first = queuePri.begin();
        for(auto i = first+1;i != queuePri.end();i++){
            i->second -= first->second;
        }
    }

    //通过回溯找到最短路径
    vector<string> backtrack(string begin, string end) {
        vector<string> temp;
        temp.push_back(end);
        string nodeNow = end;
        while(nodeNow != begin) {
            nodeNow = prev[nodeNow].first;
            temp.insert(temp.begin(), nodeNow);
        }
        return backtrack;
    }

    vector<string> dijkstra(string begin, string end) {
        map<string,int> queuePri;
        queuePri[begin] = 0;
        while(!queuePri.empty()) {
            //到达最短的点.
            auto it = queuePri.begin();

            //更新所有节点距离信息
            update(queuePri);

            //找到最终节点
            if(it->first == end) {
                return backtrack(begin,end);
            }

            //更新可达节点信息
            for (size_t i = 0; i < graph[it->first].size(); i++) {
                //如果节点被访问过则去除这一节点.
                if(visited.find(graph[it->first][i].first)){
                    continue;
                }

                //发现新节点或者更短的路径
                if(queuePri.find(graph[it->first][i].first) != queuePri.end() || queuePri[graph[it->first][i].first] > graph[it->first][i].second) {
                    //更新新节点或者最短路径
                    queuePri[graph[it->first][i].first] = graph[it->first][i].second;
                    //绑定该节点与前驱节点,
                    prev[graph[it->first][i].first] = cityAndDistance(it->first, graph[it->first][i].second);
                }
            }

            //擦除已到达节点.
            queuePri.erase(it);
            //排序,使得每次最短的节点都在begin位置.
            sort(queuePri.begin(),queuePri.end(),compare);
        }
        return vector<string>();
    }

    vector<string> findShortestPath(vector<pair<string, string, int>> path) {
        initPointList(path);
        return dijkstra(begin, end);
    }
}
posted @ 2017-03-05 19:12  XLLL  阅读(419)  评论(0编辑  收藏  举报