数据结构 图的详细介绍
图的详细介绍
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、图的存储结构—-邻接表的优缺点
- 优点:
对于稀疏图,邻接表比邻接矩阵更节约空间。 - 缺点:
不容易判断两个顶点是否有关系(边),顶点的出度容易,但是求入度需要遍历整个邻接表。
数据结构 图的详细介绍相关推荐
- java的annotation_Java Annotation认知(包括框架图、详细介绍、示例说明)
摘要 Java Annotation是JDK5.0引入的一种注释机制. 网上很多关于Java Annotation的文章,看得人眼花缭乱.Java Annotation本来很简单的,结果说的人没说清楚 ...
- Java Annotation认知(包括框架图、详细介绍、示例说明)
摘要 Java Annotation是JDK5.0引入的一种注释机制. 网上很多关于Java Annotation的文章,看得人眼花缭乱.Java Annotation本来很简单的,结果说的人没说清楚 ...
- 用例图、活动图、时序图、类图的详细介绍
UML软件开发模型的组成包括:功能模型,动态模型以及静态模型.其中,功能模型主要指的是用例图,用来描述每个用户的职责以及其可能发出的动作:动态模型包括分析图,顺序图,主要用来描述用户的行为动作之间的先 ...
- qq音乐 (轮播图) 详细介绍
目录 QQ音乐出续集了啊 小伙伴们! 下面说一下这三块分别是怎么设置的 额外 一些细节的修改: 接下来 展示一下我的代码部分 css样式 html代码 js代码: 今日金句 QQ音乐出续集了啊 小伙伴 ...
- 新手必读:Arduino UNO R3教程,原理图,引脚图,详细介绍
刚入门的学习Arduino的朋友都会有个疑问Arduino UNO R3是什么?为什么要从Arduino UNO R3开始学起? Arduino概述: Arduino是一个开放源码电子原型平台,拥有灵 ...
- ggridges包—峰峦图详细介绍
上次可视化系列说了瀑布图.它可以用于展示拥有相同的X轴变量数据(如相同的时间序列).不同的Y轴离散型变量(如不同的类别变量)和Z轴数值变量. 本节使用的峰峦图也可以很好地展示瀑布图的数据信息.它们对于 ...
- [转]详细介绍java中的数据结构
详细介绍java中的数据结构 本文介绍的是java中的数据结构,本文试图通过简单的描述,向读者阐述各个类的作用以及如何正确使用这些类.一起来看本文吧! 也许你已经熟练使用了java.util包里面的各 ...
- python链表详细教程_详细介绍python数据结构之链表
这篇文章主要为大家详细介绍了python数据结构之链表的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 数据结构是计算机科学必须掌握的一门学问,之前很多的教材都是用C语言实现链表,因为c有 ...
- 【数据结构】图的详细分析(全)
目录 前言 1. 定义 2. 存储结构 2.1 邻接矩阵 2.2 邻接表 3. 图的遍历 3.1 深度优先搜索 3.2 广度优先搜索 4. 图的应用 4.1 最小生成树 4.1.1 普里姆算法 4.1 ...
最新文章
- android layout后还原位置,Android图片框架photoview如何记住所有状态并还原,包括缩放度,缩放后的移动的距离等等...
- 玩转SpringCloud Spring Cloud 微服务
- python整数与IP地址转换
- 从锁的原理到构建分布式锁
- Yii 中Criteria常用方法
- Beyond Compare中插入表格数据的教程
- 大学物理2-2笔记(5)麦克斯韦电磁场理论
- 用 Python 修改微信(支付宝)运动步数,轻松 TOP1
- mac虚拟机安装win10
- 合唱队形java_动态规划之合唱队形问题
- getch()功能与用法
- 92 - 青蛙跳台阶
- highCharts第一天学习笔记(面积图)
- vba 添加outlook 签名_如何在Outlook中使用宏发送邮件,并且使用已有签名?
- 人生效率手册:重塑升级版
- numpy array 报错 Layout of the output array img is incompatible with cv::Mat
- 视频剪辑必看,6个免费的音、视频素材网站
- Android 画中画(视频)
- 经纬度画轨迹图_空气质量、实时路况、出租车轨迹,城市中海量数据如何高效管理?...
- Qt程序打包——教你做一个可安装的执行程序