0
点赞
收藏
分享

微信扫一扫

c++图的DFS/联通分量/路径

西红柿上校 2022-04-03 阅读 109
python

数据结构遍历的意义

树的遍历

图的遍历

树的前序遍历



图遍历和树遍历区别

知识回顾

树的深度优先遍历



普通函数和递归函数调用的区别

图的深度优先遍历的过程


初始visited数组都是fasle,当从顶点0开始调用时, 如果没有被遍历,那么遍历结果列表中添加这个顶点,然后开始for循环,执行dfs(1)

DFS的改进

如果是如下的图结构

对应的txt

7 6
0 1
0 2
1 3
1 4
2 3
2 6

执行dfs之后我们发现 顶点5并没有被遍历,这是因为我们dfs是从0开始的
只调用一次, 5和其他顶点并没有联通, 所以应该执行dfs(5)

图的DFS的典型应用, 求联通分量的个数

#include <iostream>
#include <cassert>
#include "ReadGraph.h"
#include "SparseGraph.h"
#include "DenseGraph.h"
using namespace std;
// 求无权图的联通分量
template<typename Graph>
class Component{
private:
    Graph &G; // 图的引用
    bool *visited;  // 记录dfs的过程中节点是否被访问
    int ccount;  // 记录联通分量个数
    int *id;   // 每个节点所对应的联通分量标记
    // 图的深度优先遍历
    void dfs(int v){
        visited[v] = true;
        id[v] = ccount;
        typename Graph::adjIterator adj(G, v);
        for (int i = adj.begin(); !adj.end() ; i = adj.next()) {
            if (!visited[i])
                dfs(i);
        }
    }

public:
    // 构造函数, 求出无权图的联通分量
    Component(Graph &graph):G(graph){
        // 算法初始化
        visited = new bool[G.V()];
        id = new int[G.V()];
        ccount  = 0;
        for (int i = 0; i < G.V(); i++) {
            visited[i] = false;
            id[i] = -1;
        }
        // 求图的联通分量
//        dfs(0);
        for (int i = 0; i < G.V(); i++) {
            if (!visited[i]){
                dfs(i);
                ccount++;
            }
        }
    }
    ~Component(){
        delete[] visited;
        delete[] id;
    }
    // 返回图的联通分量个数
    int count(){
        return ccount;
    }
    // 查询点v和点w是否联通  相等代表隶属同一个联通分量
    bool isConnected(int v, int w){
        assert(v >= 0 && v < G.V());
        assert(w >= 0 && w < G.V());
        return id[v] = id[w];
    }
};
// 测试图的联通分量算法
int main (){
    string filename = "/Users/ericli/CLionProjects/lijunshi/day14/testG1.txt";
    SparseGraph g1(13, false);
    ReadGraph<SparseGraph> readGraph1(g1, filename);
    Component<SparseGraph> component1(g1);
    cout<< "------" <<endl;
    cout<< "testG1 component1 count = "<<component1.count()<<endl;

    cout<< "------" <<endl;


    DenseGraph g2(13, false);
    ReadGraph<DenseGraph> readGraph2(g2, filename);
    Component<DenseGraph> component2(g2);
    cout<< "------" <<endl;
    cout<< "testG2 component2 count = "<<component2.count()<<endl;

    cout<< "------" <<endl;
    //  使用两种图的存储方式读取testG2.txt文件


    filename = "/Users/ericli/CLionProjects/lijunshi/day13/testG2.txt";
    SparseGraph g3(6, false);
    ReadGraph<SparseGraph> readGraph3(g3, filename);
    Component<SparseGraph> component3(g3);
    cout<< "------" <<endl;
    cout<< "testG2 component3 count = "<<component3.count()<<endl;
    cout<< "------" <<endl;


    DenseGraph g4(6, false);
    ReadGraph<DenseGraph> readGraph4(g4, filename);
    Component<DenseGraph> component4(g4);
    cout<< "------" <<endl;

    cout<< "testG2  component4 count="<<component4.count()<<endl;

    cout<< "------" <<endl;
    return 0;
}

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

路径查询

新建Path.cpp


在这里插入图片描述

#include <iostream>
#include <vector>
#include <stack>
#include "ReadGraph.h"
#include "SparseGraph.h"
#include "DenseGraph.h"
using namespace std;

// 路径查询
template<typename Graph>
class Path {
private:
    Graph &G; // 图的引用
    bool *visited;  // 记录dfs的过程中节点是否被访问
    int s; // 起始点
    int *from; // 记录路径, from[i]表示查找的路径上i的上一个节点
    // [1, 2, 5]   from[2] 代表 5 是从2点过来的
    // 图的深度优先遍历
    void dfs(int v) {
        visited[v] = true;
        typename Graph::adjIterator adj(G, v);
        for (int i = adj.begin(); !adj.end(); i = adj.next()) {
            if (!visited[i]){
                from[i] = v;
                dfs(i);
            }
        }
    }

public:
    // 构造函数, 寻路算法, 寻找图graph从s点到其他点的路径
    Path(Graph &graph, int s) : G(graph) {
        assert(s >= 0 && s < G.V());
        visited = new bool[G.V()];
        from = new int[G.V()];
        for (int i = 0; i < G.V(); i++) {
            visited[i] = false;
            from[i] = -1;
        }
        this->s = s;
        // 寻路算法
        dfs(s);
    }

    // 析构函数
    ~Path() {
        delete[] visited;
        delete[] from;
    }

    // 查询从s点到w点是否有路径
    bool hasPath(int w) {
        assert(w >= 0 && w < G.V());
        return visited[w];

    }

    // 查询从s点到w点的路径, 存放在vec中
    void path(int w, vector<int> &vec) {
        assert(hasPath(w));
        stack<int> s;
        // 通过from数组逆向查找到从s到w的路径, 存放到栈中
        int p = w;
        while (p != -1) {
            s.push(p);
            p = from[p];
        }
        // 从栈中依次取出元素, 获得顺序的从s到w的路径
        vec.clear();
        while (!s.empty()) {
            vec.push_back(s.top());
            s.pop();
        }
    }

    // 打印出从s点到w点的路径
    void showPath(int w) {
        assert(hasPath(w));
        vector<int> vec;
        path(w, vec);
        for (int i = 0; i < vec.size(); i++) {
            cout << vec[i];
            if (i == vec.size() - 1)
                cout << endl;
            else
                cout << " ->";
        }
    }

};

int main() {
    string filename = "/Users/ericli/CLionProjects/lijunshi/day14/testG2.txt";
    SparseGraph g(6, false);
    ReadGraph <SparseGraph> readGraph(g, filename);
    g.show();

    Path<SparseGraph> path(g, 0);
    path.showPath(5);
    //
    return 0;
}

在这里插入图片描述

举报

相关推荐

0 条评论