鄰接表有向圖DFS


DFS簡介

對一個通用有向圖,從一個給定起始頂點開始的一個深度優先遍歷。首先訪問起始頂點,接着順着有向弧儘可能深的訪問從起始頂點可以到達並且沒有被訪問過的頂點

DFS算法

/* 對一個有向圖進行深度優先遍歷,找出從一個給定的出事頂點開始,能夠到達的所有頂點 */
(1)訪問初始頂點v。
(2)對於每個鄰接於v的頂點w做以下工作:
如果w未被訪問,將w作爲初始頂點,應用深度優先遍歷算法
EndFor


最短路徑

最短路徑簡介

尋找一個有向圖中從一個起始頂點start到一個目的頂點destination之間一條最短路徑。
顯示從start到destination的一條最短路徑上的所有頂點,如果無法從start到達destination,則給出一條提示信息

最短路徑算法

  1. 訪問start,並將其標籤置爲0
  2. 初始化distance爲0
  3. 初始化一個隊列爲僅包含start
  4. 當destination還未被訪問且頂點隊列非空,做以下工作
    1.從隊列中刪除一個頂點v
    2.如果v的標籤大於distance,將distance增1
    3.對於鄰接於v的米一個頂點w:
    如果w還未被訪問,則
    • 訪問w,並將其標籤置爲distance+1
    • 將w添加到隊列中
  5. 如果destination還未被訪問,則
    顯示”Destination not reachable from start vertex”
    否則,如下查找最短路徑中的p[0],…,p[distance]
    1.初始化p[distance]爲destination
    2.對於distance-1 ~ 0的每一個值k
    找到鄰接於p[k+1]並標籤爲k的一個頂點p[k]

完整代碼如下

所用IDE:VS2015
如果所用編譯器不支持C+11,可以將部分需要C++11特性的代碼替換

Digraph.h

//Digraph.h
#pragma once
#include <list>
#include <vector>
#include <queue>
#include <fstream>
#include <iomanip>
#include <iostream>
//鄰接表儲存有向圖
template<typename DataType>
class Digraph
{
public:
    /*返回儲存在頂點k中的數據*/
    DataType Data(int k) const;
    /*輸入操作,將有向圖的鄰接表儲存在myAdjacencyLists中*/
    void Read(std::ifstream &inStream);
    /*輸出每一個頂點以及它的鄰接表*/
    void display(std::ostream & out);
    /*從頂點start開始,通過輔助函數depthFirstSearchAux()對有向圖進行深度優先遍歷*/
    void DepthFirstSearch(int start);
    /*返回頂點個數*/
    size_t getSize()const
    {
        return myAdjacencyLists.size();
    }
    /*尋找從頂點start到目標頂點destination之間的一條最短路徑,儲存在vector<int>返回*/
    std::vector<int> shortestPath(int start, int destination);

private:
    /*鄰接表的頭節點*/
    class VertexInfo
    {
    public:
        DataType data;
        std::list<int> adjacencyList;
    };
    std::vector<VertexInfo> myAdjacencyLists;
    /*從start開始對有向圖進行深度優先遍歷*/
    void depthFirstSearchAux(int start, std::vector<bool> & unvisted);

};

template <typename DataType>
DataType Digraph<DataType>::Data(int k) const
{
    return myAdjacencyLists[k].data;
}

template <typename DataType>
void Digraph<DataType>::Read(std::ifstream& inStream)
{
    typename Digraph<DataType>::VertexInfo vi;
    int n;      //鄰接於某個頂點的頂點個數
    int vertex; //一個頂點的編號
    /*使第0個值無效,使下標和慣例一樣從1開始*/
    myAdjacencyLists.push_back(vi);

    //創建鄰接表
    for (;;)
    {
        inStream >> vi.data;
        if (inStream.eof())
            break;
        inStream >> n;
        std::list<int> adjList; //創建一個空列表
        for (auto i = 1;i <= n;i++)
        {
            inStream >> vertex;
            adjList.push_back(vertex);
        }
        vi.adjacencyList = adjList;
        myAdjacencyLists.push_back(vi);

    }
}

template <typename DataType>
void Digraph<DataType>::display(std::ostream& out)
{
    out << "Adjacency-List Representation:\n";
    for (auto i = 1;i < getSize();i++)
    {
        out << i << ":" << std::setw(15) << std::setiosflags(std::ios::left) << myAdjacencyLists[i].data << "\t- ";
        for (auto it = myAdjacencyLists[i].adjacencyList.begin(); it != myAdjacencyLists[i].adjacencyList.end();++it)
            out << *it << "\t";
        out << std::endl;
    }
}

template <typename DataType>
void Digraph<DataType>::DepthFirstSearch(int start)
{
    std::vector<bool> unvistied(myAdjacencyLists.size(), true);
    depthFirstSearchAux(start, unvistied);
}

template <typename DataType>
void Digraph<DataType>::depthFirstSearchAux(int start, std::vector<bool>& unvisted)
{
    std::cout << myAdjacencyLists[start].data << std::endl;
    unvisted[start] = false;
    //遍歷它的鄰接表,對其中每一個未被訪問的節點執行深度優先遍歷

    //迭代器遍歷 
    //for(std::list<int>::iterator it=myAdjacencyLists[start].adjacencyList.begin();it!= myAdjacencyLists[start].adjacencyList.end();++it)

    //C++11 auto for遍歷
    for (auto &it : myAdjacencyLists[start].adjacencyList)
    {
        //檢查當前節點是否已被訪問
        if (unvisted[*it])
            depthFirstSearchAux(*it, unvisted);
    }
}

template <typename DataType>
std::vector<int> Digraph<DataType>::shortestPath(int start, int destination)
{
    size_t n = getSize();                       //頂點個數
    std::vector<int> distLable(n, -1),          //頂點的距離標籤,所有頂點都標誌成unvisited(-1)
                     predLable(n);              //頂點的前驅標籤

    //從start開始執行廣度優先遍歷來尋找destination
    //對start到目前所經過的頂點都給出距離標籤
    distLable[start] = 0;       
    auto distance = 0;              //距start頂點的距離
    int vertex;                     //一個頂點
    std::queue<int> vertexQueue;    //頂點隊列
    vertexQueue.push(start);
    while (distLable[destination] < 0 && !vertexQueue.empty())
    {
        vertex = vertexQueue.front();
        vertexQueue.pop();
        if (distLable[vertex] > distance)
            distance++;
        for (std::list< int >::iterator it = myAdjacencyLists[vertex].adjacencyList.begin(); it != myAdjacencyLists[vertex].adjacencyList.end();++it)
        {
            if (distLable[*it] < 0)
            {
                distLable[*it] = distance + 1;
                predLable[*it] = vertex;
                vertexQueue.push(*it);
            }
        }
    }

    distance++;
    //如果存在最短路徑,則開始重構這條最短路徑;反之,返回一個空向量
    std::vector<int> path(distance + 1);
    if (distLable[destination] < 0)
        std::cout << "Destination not reachable from start vertex\n";
    else
    {
        path[distance] = destination;
        for (auto k = distance - 1;k >= 0;k--)
            path[k] = predLable[path[k + 1]];
    }
    return path;
}

Digraph.cpp

#include "Digraph.h"
#include <string>

int main()
{
    std::cout << "Enter name of network file : ";

    std::string networkFile;
    std::cin >> networkFile;

    std::cout << std::endl;
    std::ifstream inFile(networkFile.data());

    if (!inFile.is_open())
    {
        std::cerr << "*** Cannot open " << networkFile << " ***\n";
        exit(-1);
    }

    Digraph<std::string> d;
    d.Read(inFile);
    std::cout << "The digraph's";
    d.display(std::cout);
    std::cout << std::endl;

    int start, destination;
    auto MaxSize = d.getSize() - 1;
    char response;

    do
    {
        std::cout << "Number of start  city ? ";
        do
        {
            std::cin >> start;
            if (start > MaxSize || start < 1)
                std::cerr << "The valid number from 1 to " << MaxSize << " : ";
        } while (start > MaxSize || start < 1);

        std::cout << "Number of destination ? ";
        do
        {
            std::cin >> destination;
            if (destination > MaxSize || destination < 1)
                std::cerr << "The valid number from 1 to " << MaxSize << " : ";
            if(destination==start)
                std::cerr << "The destination should not be " << start << ".";
        } while (destination > MaxSize || destination < 1|| destination==start);

        auto path = d.shortestPath(start, destination);
        std::cout << "Shortest path is :\n";
        for (auto k = 0;k < path.size() - 1;k++)
        {
            std::cout << std::setw(3) << path[k] << " " << d.Data(path[k]) << std::endl;
            std::cout << "       |\n"
                << "       v\n";
        }
        std::cout << std::setw(3) << destination << " " << d.Data(destination) << std::endl;
        std::cout << "\nMore(Y or N) ?";
        std::cin >> response;

    } while (response == 'Y' || response == 'y');

    return 0;
}

運行結果

Enter name of network file : NetworkFile.dat

The digraph'sAdjacency-List Representation:
1:Los_Angeles           - 3     4       6
2:San_Francisco         - 1     3       4
3:Denver                - 1     2       5
4:Chicago               - 3     8
5:Boston                - 4     6
6:New_York              - 4     7       8
7:Miami                 - 8     3       5
8:New_Orleans           - 1     7

Number of start  city ? 5
Number of destination ? 1
Shortest path is :
5   Boston
       |
       v
4   Chicago
       |
       v
3   Denver
       |
       v
1   Los_Angeles

More(Y or N) ?

測試所用數據

Network.dat

Los_Angeles
3 3 4 6
San_Francisco
3 1 3 4
Denver
3 1 2 5
Chicago
2 3 8
Boston
2 4 6
New_York
3 4  7 8
Miami
3 8 3 5
New_Orleans
2 1 7
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章