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(int, int) const = 0;
23
24 virtual void insertEdge(edge<T>*) = 0;
25
26 virtual void eraseEdge(int, int) = 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 = 0, int 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 T 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 = 0; this->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 = 0, int 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 T 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 = 0; this->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++ 邻接数组 实现四种图类的主要内容,如果未能解决你的问题,请参考以下文章