图的详细介绍

1、名词解释:

  • 图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V,E),其中,G表示一个图,V是图G中顶点的集合E是图G中边的集合。在图中的数据元素,我们称之为顶点(Vertex),顶点集合有穷非空。在图中,任意两个顶点之间都可能有关系,顶点之间的逻辑关系用边来表示,边集可以是空的
  • 图按照边的有无方向分为无向图和有向图无向图由顶点和边组成有向图由顶点和弧构成弧有弧尾和弧头之分,带箭头一端为弧头
  • 图按照边或弧的多少分稀疏图和稠密图。如果图中的任意两个顶点之间都存在边叫做完全图,有向的叫有向完全图。若无重复的边或顶点到自身的边则叫简单图
  • 图中顶点之间有邻接点、依附的概念无向图顶点的边数叫做度有向图顶点分为入度和出度
  • 图上的边或弧带有权则称为网
  • 图中顶点间存在路径两顶点存在路径则说明是连通的,如果路径最终回到起始点则称为环,当中不重复的叫简单路径。若任意两顶点都是连通的,则图就是连通图有向则称为强连通图图中有子图,若子图极大连通则就是连通分量,有向的则称为强连通分量
  • 无向图中连通且n个顶点n-1条边称为生成树(和斜树的结构类似)有向图中一顶点入度为0其余顶点入度为1的叫有向树一个有向图由若干棵有向树构成生成森林

2、图的存储结构—-邻接矩阵(两个数组,一个一维,一个二维)

图的邻接矩阵的表示方式需要两个数组表示图的信息一个一维数组表示每个数据元素的信息一个二维数组(邻接矩阵)表示图中的边或者弧的信息

如果图有n个顶点,那么邻接矩阵就是一个n*n的方阵,方阵中每个元素的值的计算公式如下:

邻接矩阵表示图的具体示例如下图所示:

首先给个无向图的实例:

下面是一个有向图的实例:

OK,到这里为止,我们给出一个无向图的邻接矩阵和一个有向图的邻接矩阵,我们可以从这两个邻接矩阵得出一些结论:

  • 无向图的邻接矩阵都是沿对角线对称
  • 要知道无向图中某个顶点的度无向图顶点的边数),其实就是这个顶点vi在邻接矩阵中第i行或(第i列)的元素之和;例如v1 的度是2
  • 对于有向图,要知道某个顶点的出度,其实就是这个顶点vi在邻接矩阵中第i行的元素之和如果要知道某个顶点的入度,那就是第i列的元素之和出度看本行的和,入度看本列的和

但是,如果我们需要表示的图是一个网的时候,例如假设有个图有n个顶点,同样该网的邻接矩阵也是一个n*n的方阵,只是方阵元素的值的计算方式不同,如下图所示:

这里的Wij表示两个顶点vi和vj边上的权值。无穷大表示一个计算机允许的、大于所有边上权值的值,也就是一个不可能的极限值。下面是具体示例,表示的一个有向网的图和邻接矩阵

3、图的存储结构—-邻接矩阵的代码实现.

#include<iostream>
using namespace std;
enum Graphkind{ DG, DN, UDG, UDN }; //{有向图,无向图,有向网,无向网}
typedef struct  Node
{int * vex;  //顶点数组
int vexnum; //顶点个数
int edge;   //图的边数
int ** adjMatrix; //图的邻接矩阵
enum Graphkind kind;
}MGraph;
void createGraph(MGraph & G,enum Graphkind kind)
{cout << "输入顶点的个数" << endl;
cin >> G.vexnum;
cout << "输入边的个数" << endl;
cin >> G.edge;
//输入种类
//cout << "输入图的种类:DG:有向图 DN:无向图,UDG:有向网,UDN:无向网" << endl;
G.kind = kind;
//为两个数组开辟空间
G.vex = new int[G.vexnum];
G.adjMatrix = new int*[G.vexnum];
cout << G.vexnum << endl;
int i;
for (i = 0; i < G.vexnum; i++)
{G.adjMatrix[i] = new int[G.vexnum];
}
for (i = 0; i < G.vexnum; i++)
{for (int k = 0; k < G.vexnum; k++){if (G.kind == DG || G.kind == DN){G.adjMatrix[i][k] = 0;}else {G.adjMatrix[i][k] = INT_MAX;}}}
/*//输入每个元素的信息,这个信息,现在还不需要使用
for (i = 0; i < G.vexnum; i++)
{cin >> G.vex[i];
}*/
cout << "请输入两个有关系的顶点的序号:例如:1 2 代表1号顶点指向2号顶点" << endl;
for (i = 0; i < G.edge; i++)
{int a, b;cin >> a;cin >> b;if (G.kind == DN) {G.adjMatrix[b - 1][a - 1] = 1;G.adjMatrix[a - 1][b - 1] = 1;}else if (G.kind == DG){G.adjMatrix[a - 1][b - 1] = 1;}else if (G.kind == UDG){int weight;cout << "输入该边的权重:" << endl;cin >> weight;G.adjMatrix[a - 1][b - 1] = weight;}else {int weight;cout << "输入该边的权重:" << endl;cin >> weight;G.adjMatrix[b - 1][a - 1] = weight;G.adjMatrix[a - 1][b - 1] = weight;}
}void print(MGraph g)
{int i, j;for (i = 0; i < g.vexnum; i++){for (j = 0; j < g.vexnum; j++){if (g.adjMatrix[i][j] == INT_MAX)cout << "∞" << " ";elsecout << g.adjMatrix[i][j] << " ";}cout << endl;}
}void clear(MGraph G)
{delete G.vex;
G.vex = NULL;for (int i = 0; i < G.vexnum; i++){delete G.adjMatrix[i];G.adjMatrix[i] = NULL;}
delete G.adjMatrix;
}
//如果报错可能是void 的g 、G没有*
int main()
{MGraph G;cout << "有向图例子:" << endl;createGraph(G, DG);print(G);clear(G);cout << endl;cout << "无向图例子:" << endl;createGraph(G, DN);print(G);clear(G);cout << endl;cout << "有向图网例子:" << endl;createGraph(G, UDG);print(G);clear(G);cout << endl;cout << "无向图网例子:" << endl;createGraph(G, UDN);print(G);clear(G);cout << endl;
return 0;
}输出结果:

4、图的存储结构—-邻接矩阵的优缺点

  • 优点:
    直观、容易理解,可以很容易的判断出任意两个顶点是否有边,最大的优点就是很容易计算出各个顶点的度
  • 缺点:
    当我表示完全图的时候,邻接矩阵是最好的表示方法,但是对于稀疏矩阵,由于它边少,但是顶点多,这样就会造成空间的浪费。(适合完全图,不适合稀疏矩阵

5、 图的存储结构—邻接表(两个结构体)

邻接表是图的一种链式存储结构主要是应对于邻接矩阵在顶点多边少的时候,浪费空间的问题。它的方法就是声明两个结构。如下图所示:

            一个头节点一个表结点

OK,我们虽然知道了邻接表是这两个结构来表示图的,那么它的怎么表示的了,不急,我们先把它转为c++代码先,然后,再给个示例,你就明白了。

typedef char Vertextype;//表结点结构
struct ArcNode {int adjvex;   //某条边指向的那个顶点的位置(一般是数组的下标)。ArcNode * nextarc; //指向下一个表结点int weight;   //这个只有网图才需要使用。普通的图可以直接忽略
};//头结点
struct Vnode
{Vertextype data;  //这个是记录每个顶点的信息(现在一般都不需要怎么使用)ArcNode * firstarc; //指向第一条依附在该顶点边的信息(表结点)
};

无向图的示例:

从图中我们可以知道,顶点是通过一个头结点类型的一维数组来保存的,其中我们每个头结点的firstarc都是指向第一条依附在该顶点边的信息,表结点的adjvex表示的该边的另外一个顶点在顶点数组中的下标,weight域对于普通图是无意义的,可以忽略,nextarc指向的是下一条依附在该顶点的边的信息。

下面再给出一个有向图的例子:

通过上述的两个例子,我们应该明白邻接表是如何进行表示图的信息的了。

一般网才用权重

6、图的存储结构—-邻接表的代码实现

#include<iostream>
#include<string>
using namespace std;typedef string Vertextype;//表结点结构
struct ArcNode {int adjvex;   //某条边指向的那个顶点的位置(一般是数组的下标)。ArcNode * nextarc; //指向下一个表结点int weight;   //这个只有网图才需要使用。
};//头结点
struct Vnode
{Vertextype data;  //这个是记录每个顶点的信息(现在一般都不需要怎么使用)ArcNode * firstarc; //指向第一条依附在该顶点边的信息(表结点)
};//图
struct Graph
{int kind;  //图的种类(有向图:0,无向图:1,有向网:2,无向网:3)int vexnum; //图的顶点数int edge;  //图的边数Vnode * node; //图的(顶点)头结点数组
};
void createGraph(Graph &g,int kind)
{cout << "请输入顶点的个数:" << endl;cin >> g.vexnum;cout << "请输入边的个数(无向图/网要乘2):" << endl;cin >> g.edge;g.kind = kind; //决定图的种类g.node = new Vnode[g.vexnum];int i;cout << "输入每个顶点的信息:" << endl;//记录每个顶点的信息for (i = 0; i < g.vexnum; i++){cin >> g.node[i].data;g.node[i].firstarc=NULL;}cout << "请输入每条边的起点和终点的编号:" << endl;for (i = 0; i < g.edge; i++){int a;int b;cin >> a; //起点cin >> b; //终点ArcNode * next=new ArcNode;next->adjvex = b - 1;if(kind==0 || kind==1)next->weight = -1;else {//如果是网图,还需要权重cout << "输入该边的权重:" << endl;cin >> next->weight;}next->nextarc = NULL;//将边串联起来if (g.node[a - 1].firstarc == NULL) {g.node[a - 1].firstarc=next;}else{ArcNode * p;p = g.node[a - 1].firstarc;while (p->nextarc)//找到该链表的最后一个表结点{p = p->nextarc;}p->nextarc = next;}}
}void print(Graph  g)
{int i;cout << "图的邻接表为:" << endl;for (i = 0; i < g.vexnum; i++){cout << g.node[i].data<<" ";ArcNode * now;now = g.node[i].firstarc;while (now){cout << now->adjvex << " ";now = now->nextarc;}cout << endl;}
}int main()
{Graph g;cout << "有向图的例子" << endl;createGraph(g,0);print(g);cout << endl;cout << "无向图的例子" << endl;createGraph(g, 1);print(g);cout << endl;return 0;
}输出结果:


7、图的存储结构—-邻接表的优缺点

  • 优点:
    对于稀疏图,邻接表比邻接矩阵更节约空间
  • 缺点:
    不容易判断两个顶点是否有关系(边),顶点的出度容易,但是求入度需要遍历整个邻接表

数据结构 图的详细介绍相关推荐

  1. java的annotation_Java Annotation认知(包括框架图、详细介绍、示例说明)

    摘要 Java Annotation是JDK5.0引入的一种注释机制. 网上很多关于Java Annotation的文章,看得人眼花缭乱.Java Annotation本来很简单的,结果说的人没说清楚 ...

  2. Java Annotation认知(包括框架图、详细介绍、示例说明)

    摘要 Java Annotation是JDK5.0引入的一种注释机制. 网上很多关于Java Annotation的文章,看得人眼花缭乱.Java Annotation本来很简单的,结果说的人没说清楚 ...

  3. 用例图、活动图、时序图、类图的详细介绍

    UML软件开发模型的组成包括:功能模型,动态模型以及静态模型.其中,功能模型主要指的是用例图,用来描述每个用户的职责以及其可能发出的动作:动态模型包括分析图,顺序图,主要用来描述用户的行为动作之间的先 ...

  4. qq音乐 (轮播图) 详细介绍

    目录 QQ音乐出续集了啊 小伙伴们! 下面说一下这三块分别是怎么设置的 额外 一些细节的修改: 接下来 展示一下我的代码部分 css样式 html代码 js代码: 今日金句 QQ音乐出续集了啊 小伙伴 ...

  5. 新手必读:Arduino UNO R3教程,原理图,引脚图,详细介绍

    刚入门的学习Arduino的朋友都会有个疑问Arduino UNO R3是什么?为什么要从Arduino UNO R3开始学起? Arduino概述: Arduino是一个开放源码电子原型平台,拥有灵 ...

  6. ggridges包—峰峦图详细介绍

    上次可视化系列说了瀑布图.它可以用于展示拥有相同的X轴变量数据(如相同的时间序列).不同的Y轴离散型变量(如不同的类别变量)和Z轴数值变量. 本节使用的峰峦图也可以很好地展示瀑布图的数据信息.它们对于 ...

  7. [转]详细介绍java中的数据结构

    详细介绍java中的数据结构 本文介绍的是java中的数据结构,本文试图通过简单的描述,向读者阐述各个类的作用以及如何正确使用这些类.一起来看本文吧! 也许你已经熟练使用了java.util包里面的各 ...

  8. python链表详细教程_详细介绍python数据结构之链表

    这篇文章主要为大家详细介绍了python数据结构之链表的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 数据结构是计算机科学必须掌握的一门学问,之前很多的教材都是用C语言实现链表,因为c有 ...

  9. 【数据结构】图的详细分析(全)

    目录 前言 1. 定义 2. 存储结构 2.1 邻接矩阵 2.2 邻接表 3. 图的遍历 3.1 深度优先搜索 3.2 广度优先搜索 4. 图的应用 4.1 最小生成树 4.1.1 普里姆算法 4.1 ...

最新文章

  1. android layout后还原位置,Android图片框架photoview如何记住所有状态并还原,包括缩放度,缩放后的移动的距离等等...
  2. 玩转SpringCloud Spring Cloud 微服务
  3. python整数与IP地址转换
  4. 从锁的原理到构建分布式锁
  5. Yii 中Criteria常用方法
  6. Beyond Compare中插入表格数据的教程
  7. 大学物理2-2笔记(5)麦克斯韦电磁场理论
  8. 用 Python 修改微信(支付宝)运动步数,轻松 TOP1
  9. mac虚拟机安装win10
  10. 合唱队形java_动态规划之合唱队形问题
  11. getch()功能与用法
  12. 92 - 青蛙跳台阶
  13. highCharts第一天学习笔记(面积图)
  14. vba 添加outlook 签名_如何在Outlook中使用宏发送邮件,并且使用已有签名?
  15. 人生效率手册:重塑升级版
  16. numpy array 报错 Layout of the output array img is incompatible with cv::Mat
  17. 视频剪辑必看,6个免费的音、视频素材网站
  18. Android 画中画(视频)
  19. 经纬度画轨迹图_空气质量、实时路况、出租车轨迹,城市中海量数据如何高效管理?...
  20. Qt程序打包——教你做一个可安装的执行程序

热门文章

  1. laravel中使用阿里云视频点播遇到的坑解决思路
  2. Vulnhub靶场渗透-CH4INRULZ_v1.0.1
  3. Js获取域名地址并截取
  4. IntelliJ IDEA 12详细开发教程(一)思想的转变与新手入门
  5. C语言实现亚当姆斯方法
  6. 《仙剑奇侠传柔情版》Java的简单实现(二)
  7. 牛客网错题总结(2)
  8. 整理Python常用库,看看哪些用过的?
  9. Inter Purley Platform feature:eSPI
  10. JAVA错误: 找不到或无法加载主类