DFS(Depth First Search)

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添加到队列中
  1. 如果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