分享

图的几种存储方式

 hdzgx 2019-11-19

图的几种存储结构:

1、邻接矩阵

2、邻接表

3、链式前向星

4、C++中vector的邻接表实现(补充)

(一)邻接矩阵

邻接矩阵是表示顶点之间相邻关系的矩阵。

邻接矩阵的好处:

(1)直观、简单、好理解

(2)方便检查任意一对定点间是否存在边

(3)方便找任一顶点的所有“邻接点”(有边直接相连的顶点)

(4)方便计算任一顶点的度

对于无向图,邻接矩阵的第i行(或第i列)非零元素(或非∞元素)的个数正好是第i个顶点的度。

对于有向图,邻接矩阵的第i行(或第i列)非零元素(或非∞元素)的个数正好是第i个顶点的出度(或入度)。

邻接矩阵的局限性:时间复杂度O(n^2),空间复杂度O(n^2)

(1)浪费空间。对于稠密图还是很合算的。

                          但是对于稀疏图(点很多而边很少)有大量无效元素。

(2)浪费时间。要确定图中有多少条边,则必须按行、按列对每个元素进行检测,所花费的时间代价很大

bb这么多,我们直接来以OJ此专题第一题为例

这题二维数组map不能用int,会爆内存,bool可以自己百度,简而言之就是用于逻辑判断,只有true和false两种情况。

bool类型在存储二值变量,或者说只有真假时,更具优势,因为只有0和1即false和true,省空间

(int型的0和1都是4字节,bool都是1字节)

  1. #include<stdio.h>
  2. #include<string.h>
  3. #include<stdbool.h>
  4. bool map[5001][5001];
  5. int main()
  6. {
  7. int n,m;
  8. int u,v;
  9. int q;
  10. while(~scanf("%d %d",&n,&m))
  11. {
  12. memset(map,false,sizeof(map));
  13. while(m--)
  14. {
  15. scanf("%d %d",&u,&v);
  16. map[u][v]=true;
  17. }
  18. scanf("%d",&q);
  19. while(q--)
  20. {
  21. scanf("%d %d",&u,&v);
  22. if(map[u][v])
  23. {
  24. printf("Yes\n");
  25. }else
  26. {
  27. printf("No\n");
  28. }
  29. }
  30. }
  31. return 0;
  32. }

(二)邻接表

图的邻接表存储方法是一种顺序分配与链式分配相结合的存储方法。

在邻接表中,对图中每个顶点建立一个单链表,第i个单链表中的节点表示依附于顶点i的边(对有向图是以顶点i为尾的边)。每个单链表上附设一个表头节点。

邻接表的特点如下:

(1)邻接表表示不唯一。这是因为在每个顶点对应的单链表中,各边节点的链接次序可以是任意的,取决于建立邻接表的算法以及边的输入次序。

(2)对于有n个顶点和e条边的无向图,其邻接表有n个顶点节点和2e个边节点。显然,在总的边数小于n(n-1)/2的情况下,邻接表比邻接矩阵要节省空间。

(3)对于无向图,邻接表的顶点i对应的第i个链表的边节点数目正好是顶点i的度。

(4)对于有向图,邻接表的顶点i对应的第i个链表的边节点数目仅仅是顶点i的出度。其入度为邻接表中所有adjvex域值为i的边节点数目。
 

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. struct node
  5. {
  6. int data;
  7. struct node *next;
  8. };
  9. int main()
  10. {
  11. int n, u, j, i, v;
  12. struct node *p, *a[5050];
  13. while(~scanf("%d", &n))
  14. {
  15. for(i = 0; i < n; i++)
  16. a[i] = NULL;
  17. for(i = 0; i < n; i++)
  18. {
  19. for(j = 0; j < n; j++)
  20. {
  21. scanf("%d", &u); //审清题意
  22. if(u == 1)
  23. {
  24. if(a[i] == NULL)
  25. {
  26. p = (struct node *)malloc(sizeof(struct node));
  27. p -> data = j;
  28. p -> next = NULL;
  29. a[i] = p;
  30. }
  31. else
  32. {
  33. p = (struct node *)malloc(sizeof(struct node));
  34. p -> next = a[i] -> next;
  35. p -> data = j;
  36. a[i] -> next = p;
  37. }
  38. }
  39. }
  40. }
  41. int q;
  42. scanf("%d", &q);
  43. while(q--)
  44. {
  45. scanf("%d%d", &u, &v);
  46. p = a[u];
  47. int flag = 0;
  48. while(p)
  49. {
  50. if(p -> data == v)
  51. {
  52. flag = 1;
  53. break;
  54. }
  55. p = p -> next;
  56. }
  57. if(flag)
  58. printf("Yes\n");
  59. else
  60. printf("No\n");
  61. }
  62. }
  63. return 0;
  64. }

(三)链式前向星

 https://blog.csdn.net/Binary_Heap/article/details/78209086

1. 结构

这里用两个东西:

1 结构体数组edge存边,edge[i]表示第i条边,

2 head[i]存以i为起点的第一条边(在edge中的下标)

  1. struct edge{
  2. int to; //这条边的终点
  3. int w; //权值
  4. int next; //下一条边的存储下标(默认0)
  5. }e[500010];

 

2.增边

若以点u为起点的边新增了一条,在edge中的下标为cnt.

那么edge[++cnt].next=head[u];然后head[u]=cnt.

即每次新加的边作为第一条边,最后倒序遍历

  1. void add(int u, int v, int w)
  2. {
  3. e[cnt].to = v;
  4. e[cnt].w = w;
  5. e[cnt].next = head[u];//cnt为边的计数,从1开始计
  6. head[u] = cnt++; //第一条边为当前边
  7. }

 

3. 遍历

遍历以st为起点的边

for(int i=head[st]; i!=0; i=edge[i].next)

 i 开始为第一条边,每次指向下一条(以0为结束标志)  (若下标从0开始,next应初始化-1)

链式前向星主要是用来优化的,可以用来优化BFS,SPFA算法等等。在做最短路的时候一直T就是因为没有用链式前向星存边导致的超时,所以这个坑我先和你们说下。

(四)vector的邻接表(补充)

vector是C++STL里面的一个东西,简单的来说就是一个可变长的数组,你可以通过往它里面压入数据来使它变长,

想深入了解的可以自己百度一波,还是以这道题为例:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<vector>//注意这个头文件不能丢,要么你就去用万能头
  4. using namespace std;
  5. #define N 500001
  6. vector<int>MAP[N];//可以理解为二维的,每个MAP可以包含若干项,这些项是以当前编号点为起点
  7. int main()
  8. {
  9. int n,m,u,v,i;
  10. while(~scanf("%d %d",&n,&m))
  11. {
  12. while(m--)
  13. {
  14. scanf("%d %d",&u,&v);
  15. MAP[u].push_back(v);//扩充
  16. }
  17. int q;
  18. scanf("%d",&q);
  19. while(q--)
  20. {
  21. scanf("%d %d",&u,&v);
  22. int len=MAP[u].size();
  23. int flag=0;
  24. for(i=0;i<len;i++)
  25. {
  26. if(MAP[u][i]==v)
  27. {
  28. flag=1;
  29. }
  30. }
  31. if(flag)
  32. {
  33. cout<<"Yes"<<endl;
  34. }else
  35. {
  36. cout<<"No"<<endl;
  37. }
  38. }
  39. for(int i=0; i<N; i++)
  40. {
  41. MAP[i].clear();
  42. }
  43. }
  44. return 0;
  45. }

嗯,目前我用过的图的几种存储方式就这样啦,谢谢大家观赏拙作。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多