图的存储

前提:

在 OI 中,想要对图进行操作,就需要先学习图的存储方式。

假设有一个有图,有n个点,m条边,从点到另一点的边权为w,现要存储这个图共有几种办法?

 

方法一:邻接矩阵

首先,定义一个n*n的数组data,data[ i ][ j ]表示从点i到点j是否连通。

1 int data[n][n]=0;

接着输入每一条边并存入数组。

1 int x,y,z;
2 for(int i=1; i<=m; i++)
3 {
4     cin >> x >> y >> z;
5     data[x][y] = z;
6 }

ps:若为无向图,则data[ y ][ x ]再赋值。若无边权,则不用输入z,data[ x ][ y ]赋值为1即可。

这样就写好啦~ 然后是一些使用场景。

1.判断点i与点j间是否有边:

1 bool pd(int a,int b)//判断点a与点b间是否有边
2 {
3     return data[a][b];
4 }

操作极其方便简单~

2.判断各顶点的度:

 1 int r1(int a)//判断入度
 2 {
 3     int t=0;
 4     for(int i=1; i<=n; i++)
 5     {
 6         if(data[i][a]) t++;
 7     }
 8     return t;
 9 }
10 
11 int r2(int a)//判断出度
12 {
13     int t=0;
14     for(int i=1; i<=n; i++)
15     {
16         if(data[a][i]) t++;
17     }
18     return t;
19 }

也不难诶~

优点:可快速判断两点间是否有边,便于计算各顶点的度;

缺点:不便于增减定点,不便于快速访问一个点的邻接点,空间复杂度高。

完整代码:

 1 #include<bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 int n,m;//顶点数与边数 
 6 int data[105][105];//矩阵 
 7 int x,y,z;
 8 
 9 bool pd(int a,int b)//判断点a与点b间是否有边
10 {
11     return data[a][b];//若有边则返回1,否则就是0 
12 }
13 
14 int r1(int a)//判断入度
15 {
16     int t=0;//统计入度
17     for(int i=1; i<=n; i++)
18     {
19         if(data[i][a]) t++;//data[i][a]判断点i是否通向点a,若不为0,则有边,加1
20     }
21     return t;
22 }
23 
24 int r2(int a)//判断出度
25 {
26     int t=0;//统计出度
27     for(int i=1; i<=n; i++)
28     {
29         if(data[a][i]) t++;//判断点a是否通向点i
30     }
31     return t;
32 }
33 
34 int main()
35 {
36     memset(data,0,sizeof(data));
37     //若data数组定义在main函数内,则必须赋值;本代码定义在main函数外,默认为0,此句可删
38     cin >> n >> m;
39     for(int i=1; i<=m; i++)
40     {
41         cin >> x >> y >> z;
42         data[x][y] = z;
43         //若为无向图,则data[y][x]再赋值。若无边权,则不用输入z,data[x][y]赋值为1即可
44     }
45     int a,b;
46     for(int i=1; i<=m; i++)//此处m可改,比0大就行 
47     {
48         cin >> a >> b;
49         if(pd(a,b)) cout << "" << endl;
50         else cout << "" << endl;
51     }
52     for(int i=1; i<=n; i++)//n个顶点的度
53     {
54         cout << i << "的入度为" << r1(i) << ",出度为" << r2(i) << endl;
55     }
56     return 0;
57 }

以上结束(^-^)

 

方法二:边集数组

首先,定义一个结构体,以存储边的起始点和边权。

1 struct kk{
2     int u,v,w;
3 }e[105];//依旧视m决定

 输入大家都会,输入我懒得写

那赋值我也不写了~

那么接下来写出下图的边集数组:

 

 

u v w
1 a b 2
2 a c 5
3 b d 6
4 b c 2
5 c d 7
6 c e 1
7 d c 2
8 d e 4

 

 

 

 

 

 

 

 

 

 

顺序输入如上表,但如果题目黑心不顺序输入怎么办呢?

排序吧~

 1 struct kk{
 2     int u,v,w;
 3     friend bool operator <(kk a,kk b)
 4     {
 5         return a.w<b.w;
 6     }
 7 }e[105];//依旧视m决定
 8 
 9 bool cmp(kk a,kk b)
10 {
11     if(a.u==b.u) return a.v<b.v;
12     return a.u<b.u;
13 }

然后就差不多啦~

优点:可对边按边权、边的大小进行排序或处理(所以常用于最小生成树);

缺点:不便于判断两点间是否有边,不便于快速访问一个点的邻接点,不便于计算各顶点的度。

代码:

 1 #include<bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 int n,m;//顶点数与边数 
 6 struct kk{
 7     int u,v,w;
 8     friend bool operator <(kk a,kk b)
 9     {
10         return a.w<b.w;
11     }
12 }e[105];//依旧视m决定
13 int x,y,z;
14 
15 bool cmp(kk a,kk b)
16 {
17     if(a.u==b.u) return a.v<b.v;
18     return a.u<b.u;
19 }
20 
21 int main()
22 {
23     cin >> n >> m;
24     for(int i=1; i<=m; i++)
25     {
26         cin >> x >> y >> z;
27         if(x>y) swap(x,y);//无向图才有的操作~ 
28         e[i].u = x;
29         e[i].v = y;
30         e[i].w = z;
31     }
32     return 0;
33 }

OK啦~

 

方法三:链式前向星

我写过(^—^)~

详细链接:链式前向星

 

方法四:邻接表(动态数组)

首先,用结构体创建一个vector数组。

1 struct kk
2 {
3     int to;//下一个点
4     int w;//边权
5 };
6 
7 vector <kk> e[105];//e[i]中i表示出发点

然后,输入我就不写啦

接着,将输入数据存入vector数组~

1 while(m--)
2 {
3     kk t;
4     cin >>temp >> t.to >> t.w;//输入出发点,终点,边权
5     e[temp].push_back(t);//将数据存入动态数组,表示在这个出发点下的所有边
6     //就相当于二维动态数组
7 }

就结束啦(^_^)

输出看看~

1 for(int i=1; i<=n; i++)//按照出发点的顺序遍历
2 {
3     for(int j=0; j<e[i].size(); j++)//遍历出发点下的所有边
4     {
5         kk t=e[i][j];//以二维数组形式输出
6         cout << "" << i <<"" << t.to << " 的边权为 " << t.w << endl;
7     }
8 }

优点:便于增删顶点,便于快速访问一个点的邻接点,空间复杂度低(这条最重要!!!)

缺点:不便于判断两点间是否有边,不便于计算各顶点的度。

代码:

 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 struct kk
 6 {
 7     int to;//下一个点
 8     int w;//边权
 9 };
10 
11 vector <kk> e[105];//e[i]中i表示出发点
12 int n,m;
13 int temp;//出发点
14 
15 int main()
16 {
17     cin >> n >> m;
18     while(m--)
19     {
20         kk t;
21         cin >>temp >> t.to >> t.w;//输入出发点,终点,边权
22         e[temp].push_back(t);//将数据存入动态数组,表示在这个出发点下的所有边
23         //就相当于二维动态数组
24     }
25     for(int i=1; i<=n; i++)//按照出发点的顺序遍历
26     {
27         for(int j=0; j<e[i].size(); j++)//遍历出发点下的所有边
28         {
29             kk t=e[i][j];//以二维数组形式输出
30             cout << "" << i <<"" << t.to << " 的边权为 " << t.w << endl;
31         }
32     }
33     return 0;
34 }

就没啦~~~~~~

谢谢观看(^ —^)

 

posted on 2022-07-03 19:29  kkk05  阅读(59)  评论(0编辑  收藏  举报

导航