C++ 邻接数组 实现四种图类

Posted 布沃布图

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++ 邻接数组 实现四种图类相关的知识,希望对你有一定的参考价值。

  • 基类 graph

  • 无权无向图 arrayGraph

    • 读入函数

    • 输出

    • 无权无向边 _edge

    • 该图类私有成员变量

    • 构造函数

    • 插入新边的函数

    • 删除一条边的函数

    • 判断有无回路的函数

    • 基于 c++ 文件流的读入输出函数

    • 其他函数方法

  • 加权有向图 arrayWDigraph

    • 读入函数

    • 输出

    • 加权有向边 WDedge

    • 该图类私有成员变量

    • 构造函数

    • 插入新边的函数

    • 删除一条边的函数

    • 基于 c++ 文件流的读入输出函数

    • 其他函数方法

  • 加权无向图 arrayWGraph

  • 无权有向图 arrayDigraph

该项目基于c++实现,运用数据结构知识完成

基类 graph

graph 基类为该书中程序 16-1 部分代码

 1template<class T>
2class edge {

3public:
4    virtual ~edge() {}
5
6    virtual int vertex1() const 0;
7
8    virtual int vertex2() const 0;
9
10    virtual T weight() const 0;
11};
12
13template<class T>
14class graph {

15public:
16    virtual ~graph() {}
17
18    virtual int numberOfVertices() const 0;
19
20    virtual int numberOfEdges() const 0;
21
22    virtual bool existsEdge(intint) const 0;
23
24    virtual void insertEdge(edge<T>*) 0;
25
26    virtual void eraseEdge(intint) 0;
27
28    virtual int degree(int) const 0;
29
30    virtual int inDegree(int) const 0;
31
32    virtual int outDegree(int) const 0;
33
34    virtual bool directed() const 0;
35
36    virtual bool weighted() const 0;
37
38    virtual void output() const 0;
39};

继承需要将父类中所有纯虚函数在子类中重新实现~

因为 insertEdge(edge<T>*) 中参数为edge类,故在继承 graph 类前要写出继承 edge 类的子类。

无权无向图 `arrayGraph`

无权无向边 _edge

没什么难度,照着父类写即可

无权边,肯定是int类,故返回权重的函数直接返回 0 即可

 1template<class T>
2class _edge :
 public edge<int> {
3public:
4    ~_edge() {}
5
6    _edge(int s = 0int e = 0) {
7        v1 = s;
8        v2 = e;
9    }
10
11    int vertex1() const {
12        return v1;
13    }
14
15    int vertex2() const {
16        return v2;
17    }
18
19    weight() const {
20        return 0;
21    }
22
23private:
24    int v1, v2;
25};

该图类私有成员变量

顶点的数量,边的数量,还有存储该图类的邻接数组。

1private:
2    int num_vertice, num_edge;
3    vector<vector<int>> Array;

构造函数

初始化三个私有成员变量即可

记得加一个判断顶点数是否合法的语句

 1arrayGraph(int nv = 0) {
2    if (nv < 0//如果顶点数小于0则抛出异常
3        throw "number of vertices must be >= 0";
4    num_edge = 0;
5    num_vertice = nv;
6    for (int i = 0; i < num_vertice; ++i) {
7        vector<int> temp;
8        Array.push_back(temp);
9    }
10}

插入新边的函数

为了避免用无参构造函数定义的图类的 num_vertice 属性在邻接数组声明大小后赋值

加一个判断语句,若邻接数组大小不合适,便重新初始化一个邻接数组

再进行插入新边的操作~

 1void insertEdge(edge<T> *e) {
2    if (Array.size() != num_vertice) {
3        Array.clear();
4        for (int i = 0; i < num_vertice; ++i) {
5            vector<int> temp;
6            Array.push_back(temp);
7        }
8    }
9    int a = e->vertex1();
10    int b = e->vertex2();
11    if (a < 0 || a > num_vertice || b < 0 || b > num_vertice) {
12        std::ostringstream s;
13        s << "no vertex ";
14        throw s.str();
15    }
16    if (!existsEdge(a, b)) {
17        Array[a].push_back(b);
18        Array[b].push_back(a);
19        num_edge++;
20    }
21}

因为是无向图,故添加新边 a~b 要在 a,b 所对应的邻接数组中分别添加 b 和 a 邻接点

最后一定要记得用 num_edge++ 及时更新边的数量。

删除一条边的函数

因为是无向图,故删除边 a~b 要在 a,b 所对应的邻接数组中分别删除 b 和 a 邻接点

 1//删除一条边
2void eraseEdge(int a, int b) {
3    if (a < 0 || a > num_vertice || b < 0 || b > num_vertice) {
4        std::ostringstream s;
5        s << "no vertex ";
6        throw s.str();
7    }
8    for (int i = 0; i < Array[a].size(); ++i) {
9        if (Array[a][i] == b)
10            Array[a].erase(Array[a].begin() + i);
11    }
12    for (int i = 0; i < Array[b].size(); ++i) {
13        if (Array[b][i] == a)
14            Array[b].erase(Array[b].begin() + i);
15    }
16}

判断有无回路的函数

使用一个 bool 类型数组标记该顶点是否被访问过

然后利用深度优先搜索函数判环

 1//判环
2bool cycle() const {
3    if (num_edge >= num_vertice)return true;
4    //边数大于顶点数必存在环
5    vector<bool> vis(num_vertice, false);
6    return dfs(vis, 0);
7}
8//深度优先搜索函数
9bool dfs(vector<bool> vis, int u, int p = -1) const
10//u表示当前访问的节点,p表示的是它的前继节点。
11
{
12    if (vis[u])//出现访问过的节点,说明有环。返回上一层节继续搜索
13        return true;
14    vis[u] = true;
15    int m = Array[u].size();
16    for (int i = 0; i < m; i++) {
17        int v = Array[u][i];
18        if (v != p)
19            //如果当前节点不等于它的前继的话,才访问。
20            if (dfs(vis, v, u))
21                return true;
22    }
23    return false;
24}

基于 c++ 文件流的读入输出函数

读入函数

文件流相关方法自行百度即可

插入新边要先构造一个 _edge 类对象,再进行插入

 1//读入
2void in() {
3    ifstream ifs;
4    ifs.open(FILENAME, ios::in);//读文件
5    if (!ifs.is_open())//判断是否存在
6    {
7        cout << "data file was not found" << endl;
8        ifs.close();
9        return;
10    }
11    int ne, nv, x1, x2;
12    ifs >> nv >> ne;
13    this->num_vertice = nv;
14    for (int i = 0this->num_edge < ne; ++i) {
15        ifs >> x1 >> x2;
16        _edge<T> t(x1, x2);
17        _edge<T> *e = &t;
18        this->insertEdge(e);
19    }
20    cout << "读取文件成功!" << endl;
21    ifs.close();
22}

输出

记得输出顶点数量及边的数量即可

 1//输出
2void output() const {
3    ofstream ofs;
4    ofs.open(FILENAME2, ios::out);
5    ofs << this->num_vertice << " " << this->num_edge << endl;
6    for (int i = 0; i < this->num_vertice; ++i) {
7        for (int j = 0; j < this->Array[i].size(); ++j) {
8            ofs << i << " " << Array[i][j] << endl;
9        }
10        ofs << endl;
11    }
12}

其他函数方法

其他方法没什么坑了,知道函数名意思就能写出来~

 1//返回顶点个数
2int numberOfVertices() const {
3    return num_vertice;
4}
5
6//返回边的条数
7int numberOfEdges() const {
8    return num_edge;
9}
10
11//判断边是否存在
12bool existsEdge(int a, int b) const {
13    if (a < 0 || a > num_vertice || b < 0 || b > num_vertice) {
14        std::ostringstream s;
15        s << "no vertex ";
16        throw s.str();
17    }
18    for (int i = 0; i < Array[a].size(); ++i) {
19        if (Array[a][i] == b)return true;
20    }
21}
22
23//度数
24int degree(int i) const {
25    if (i < 0 || i > num_vertice) {
26        std::ostringstream s;
27        s << "no vertex " << i;
28        throw s.str();
29    }
30    return Array[i].size();
31}
32
33//入度
34int inDegree(int k) const {
35    if (k < 0 || k > num_vertice) {
36        std::ostringstream s;
37        s << "no vertex " << k;
38        throw s.str();
39    }
40    return 0;
41}
42
43//出度
44int outDegree(int k) const {
45    if (k < 0 || k > num_vertice) {
46        std::ostringstream s;
47        s << "no vertex " << k;
48        throw s.str();
49    }
50    return 0;
51}
52
53//是否有向
54bool directed() const {
55    return false;
56}
57
58//是否加权
59bool weighted() const {
60    return false;
61}

加权有向图 arrayWDigraph

加权有向边 WDedge

同样没什么难度,照着父类写即可

加权边,权值类型不确定,此处使用模板类

 1template<class T>
2class WDedge :
 public edge<T> {
3public:
4    ~WDedge() {}
5
6    WDedge(int s = 0int e = 0, T we = 0) {
7        v1 = s;
8        v2 = e;
9        w = we;
10    }
11
12    int vertex1() const {
13        return v1;
14    }
15
16    int vertex2() const {
17        return v2;
18    }
19
20    weight() const {
21        return w;
22    }
23
24private:
25    int v1, v2;
26    T w;
27};

该图类私有成员变量

依旧是顶点的数量,边的数量,还有存储该图类的邻接数组。

因为是加权图,所以邻接数组为 int,T 的数对类型,int 为邻接点,T 为该边权值

1private:
2    int num_vertice, num_edge;
3    vector<vector<pair<int, T>>> Array;

构造函数

初始化三个私有成员变量即可

 1//构造函数
2arrayWDigraph(int nv = 0) {
3    if (nv < 0)                         //如果顶点数小于0则抛出异常
4        throw "number of vertices must be >= 0";
5    num_edge = 0;
6    num_vertice = nv;
7    Array.clear();
8    for (int i = 0; i < num_vertice; ++i) {
9        vector<pair<int, T>> temp;
10        Array.push_back(temp);
11    }
12}

记得加一个判断顶点数是否合法的语句

插入新边的函数

为了避免用无参构造函数定义的图类的 num_vertice 属性在邻接数组声明大小后赋值

加一个判断语句,若邻接数组大小不合适,便重新初始化一个邻接数组

再进行插入新边的操作~

 1//插入一条边
2void insertEdge(edge<T> *e) {
3    if (Array.size() != num_vertice) {
4        Array.clear();
5        for (int i = 0; i < num_vertice; ++i) {
6            vector<pair<int, T>> temp;
7            Array.push_back(temp);
8        }
9    }
10    int a = e->vertex1(), b = e->vertex2();
11    if (a < 0 || a > num_vertice || b < 0 || b > num_vertice) {
12        std::ostringstream s;
13        s << "no vertex ";
14        throw s.str();
15    }
16    if (!existsEdge(a, b)) {
17        Array[a].push_back({b, e->weight()});
18        num_edge++;
19    }
20}

最后一定要记得用 num_edge++ 及时更新边的数量。

删除一条边的函数

因为是有向图,故删除边 a~b 只需删除顶点 a 的邻接点 b 即可

 1//删除一条边
2void eraseEdge(int a, int b) {
3    if (a < 0 || a > num_vertice || b < 0 || b > num_vertice) {
4        std::ostringstream s;
5        s << "no vertex ";
6        throw s.str();
7    }
8    for (int i = 0; i < Array[a].size(); ++i) {
9        if (Array[a][i].first == b)
10            Array[a].erase(Array[a].begin() + i);
11    }
12}

基于 c++ 文件流的读入输出函数

读入函数

文件流相关方法自行百度即可

插入新边要先构造一个 WDedge 类对象,再进行插入

 1void in() {
2    ifstream ifs;
3    ifs.open(WDFILENAME, ios::in);//读文件
4    if (!ifs.is_open())//判断是否存在
5    {
6        cout << "data file was not found" << endl;
7        ifs.close();
8        return;
9    }
10    int ne, nv, x1, x2;
11    T w;
12    ifs >> nv >> ne;
13    this->num_vertice = nv;
14    for (int i = 0this->num_edge < ne; ++i) {
15        ifs >> x1 >> x2 >> w;
16        WDedge<T> t(x1, x2, w);
17        WDedge<T> *e = &t;
18        this->insertEdge(e);
19    }
20    cout << "读取文件成功!" << endl;
21    ifs.close();
22}

输出

记得输出顶点数量及边的数量即可

因为加权,一行输出三个数据分别表示起始点,终点及该边权值。

 1//输出
2void output() const {
3    ofstream ofs;
4    ofs.open(WDFILENAME2, ios::out);
5    ofs << this->num_vertice << " " << this->num_edge << endl;
6    for (int i = 0; i < this->num_vertice; ++i) {
7        for (int j = 0; j < this->Array[i].size(); ++j) {
8            ofs << i << " " << Array[i][j].first << " " << Array[i][j].second << endl;
9        }
10        ofs << endl;
11    }
12}

其他函数方法

其他方法没什么坑了,知道函数名意思就能写出来~

 1//返回顶点个数
2int numberOfVertices() const {
3    return num_vertice;
4}
5
6//返回边的条数
7int numberOfEdges() const {
8    return num_edge;
9}
10
11//判断边是否存在
12bool existsEdge(int a, int b) const {
13    if (a < 0 || a > num_vertice || b < 0 || b > num_vertice) {
14        std::ostringstream s;
15        s << "no vertex ";
16        throw s.str();
17    }
18    for (int i = 0; i < Array[a].size(); ++i) {
19        if (Array[a][i].first == b)return true;
20    }
21    return false;
22}
23
24//度数
25int degree(int i) const {
26    return this->outDegree(i) + this->inDegree(i);
27}
28
29//入度
30int inDegree(int k) const {
31    if (k < 0 || k > num_vertice) {
32        std::ostringstream s;
33        s << "no vertex " << k;
34        throw s.str();
35    }
36    int res = 0;
37    for (int i = 0; i < num_vertice; ++i) {
38        if (i != k) {
39            for (int j = 0; j < Array[i].size(); ++j) {
40                if (Array[i][j].first == k)
41                    res++;
42            }
43        }
44    }
45    return res;
46}
47
48//出度
49int outDegree(int a) const {
50    if (a < 0 || a > num_vertice) {
51        std::ostringstream s;
52        s << "no vertex " << a;
53        throw s.str();
54    }
55    return Array[a].size();
56}
57
58//是否有向
59bool directed() const {
60    return true;
61}
62
63//是否加权
64bool weighted() const {
65    return true;
66}

加权无向图 arrayWGraph

无权有向图 arrayDigraph

至于剩下两种图类大同小异,通过前两种图类的学习再来完成简直有手就行C++ 邻接数组 实现四种图类C++ 邻接数组 实现四种图类C++ 邻接数组 实现四种图类C++ 邻接数组 实现四种图类




扫码关注公众号【 布沃布图
后台回复 【 图类 】 获取源码

扫描二维码

获取更多精彩

布沃布图

以上是关于C++ 邻接数组 实现四种图类的主要内容,如果未能解决你的问题,请参考以下文章

c++实验9 图及图的操作实验

BFS(广度优先搜索)邻接矩阵C ++

B00015 C++实现的图类

基础图论总结

我需要将我的数组样式的邻接矩阵转换为矢量样式(使其看起来更好)

20182308 华罗晗 2019-2020-1 《数据结构与面向对象程序设计》第10周学习总结