【algo&ds】6.图及其存储结构、遍历
1.什么是图
图表示”多对多”的关系
包含
一组顶点:通常用 V(Vertex)表示顶点集合
一组边:通常用 E(Edge)表示边的集合
边是顶点对:(v,w)∈ E,其中 v,w ∈ V ,v—w
有向边 <v,w> 表示从 v 指向 w 的边(单行线) v→w
不考虑重边和自回路
常见术语
无向图:图中所有的边无所谓方向
有向图:图中的边可能是双向,也可能是单向的,方向是很重要的
权值:给图中每条边赋予的值,可能有各种各样的现实意义
网络:带权值的图
邻接点:有边直接相连的顶点
出度:从某顶点发出的边数
入度:指向某顶点的边数
稀疏图:顶点很多而边很少的图
稠密图:顶点多边也多的图
完全图:对于给定的一组顶点,顶点间都存在边
抽象数据类型定义
类型名称:图(Graph)
数据对象集:G(V,E)由一个非空的有限顶点集合 V 和一个有限边集合 E 组成
操作集:对于任意图 G ∈ Graph,以及 v ∈ V,e ∈ E
主要操作有:
Graph Crate():建立并返回空图
Graph InsertVertex(Graph G,Vertex v):将 v 插入 G
Graph InsertEdge(Graph G,Edge e):将 e 插入 G
void DFS(Graph G,Vertex v):从顶点 v 出发深度优先遍历图 G
void BFS(Graph G,Vertex v):从顶点 v 出发宽度优先遍历图 G
2.图的存储结构表示
2.1邻接矩阵表示
邻接矩阵 G[N][N]——N 个顶点从 0 到 N-1 编号
存在边<vi,vj>
,则G[i][j]=1,否则为0
特征:
对角线元素全 0
关于对角线对称
优点:
直观、简单、好理解
方便检查任意一对顶点间是否存在边
方便找任一顶点的所有邻接点
方便计算任一顶点的度
无向图:对应行(或列)非 0 元素的个数
有向图:对应行非 0 元素的个数是出度;对应列非 0 元素的个数是入度
缺点:
浪费空间——存稀疏图
浪费时间——统计稀疏图的边
代码实现
#include<stdio.h>
#include<stdlib.h>
#define MaxVertexNum 100
typedef int weightType;
typedef int Vertex;
typedef int DataType;
typedef struct GNode *ptrToGNode;
struct GNode{ // 图
int Nv; // 顶点数
int Ne; // 边数
weightType G[MaxVertexNum][MaxVertexNum];
DataType Data[MaxVertexNum]; // 存顶点的数据
};
typedef ptrToGNode MGraph;
typedef struct ENode *ptrToENode;
struct ENode{ // 边
Vertex V1,V2; // 有向边<V1,V2>
weightType Weight; // 权重
};
typedef ptrToENode Edge;
// 初始化图
MGraph Create(int VertexNum){
Vertex v,w;
MGraph Graph;
Graph = (MGraph)malloc(sizeof(struct GNode));
Graph->Nv = VertexNum;
Graph->Ne = 0;
for(v=0;v<VertexNum;v++)
for(w=0;w<VertexNum;w++)
Graph->G[v][w] = 0;
return Graph;
}
// 插入边
MGraph Insert(MGraph Graph,Edge E){
// 插入边 <V1,V2>
Graph->G[E->V1][E->V2] = E->Weight;
// 如果是无向图,还需要插入边 <V2,V1>
Graph->G[E->V2][E->V1] = E->Weight;
}
// 建图
MGraph BuildGraph(){
MGraph Graph;
Edge E;
Vertex V;
int Nv,i;
scanf("%d",&Nv); // 读入顶点数
Graph = Create(Nv);
scanf("%d",&(Graph->Ne)); // 读入边数
if(Graph->Ne != 0){
E = (Edge)malloc(sizeof(struct ENode));
for(i=0;i<Graph->Ne;i++){
scanf("%d %d %d",&E->V1,&E->V2,&E->Weight); // 读入每个边的数据
Insert(Graph,E);
}
}
return Graph;
}
// 遍历图
void print(MGraph Graph){
Vertex v,w;
for(v=0;v<Graph->Nv;v++){
for(w=0;w<Graph->Nv;w++)
printf("%d ",Graph->G[v][w]);
printf("\n");
}
}
int main(){
MGraph Graph;
Graph = BuildGraph();
print(Graph);
return 0;
}
2.2邻接表实现
特点:
- 方便找任一顶点的所有邻接顶点
- 节省稀疏图的空间
- 需要 N 个头指针 + 2E 个结点(每个结点至少 2 个域)
- 对于是否方便计算任一顶点的度
- 无向图:方便
- 有向图:只能计算出度
- 不方便检查任意一对顶点间是否存在边
代码实现
#include<stdio.h>
#include<stdlib.h>
#define MaxVertexNum 100
typedef int Vertex;
typedef int DataType;
typedef int weightType;
typedef struct ENode *ptrToENode;
struct ENode{ // 边
Vertex V1,V2; // 有向边<V1,V2>
weightType Weight; // 权重
};
typedef ptrToENode Edge;
typedef struct AdjVNode *ptrToAdjVNode;
struct AdjVNode{ // 邻接表内元素
Vertex AdjV; // 邻接点下标
weightType Weight; // 权值
ptrToAdjVNode Next; // 下一个
};
typedef struct VNode{ // 邻接表头
ptrToAdjVNode FirstEdge; // 存每个顶点指针
DataType Data; // 顶点数据
}AdjList[MaxVertexNum];
typedef struct GNode *ptrToGNode;
struct GNode{ // 图
int Nv; // 顶点
int Ne; // 边数
AdjList G; // 邻接表
};
typedef ptrToGNode LGraph;
// 初始化
LGraph create(int VertexNum){
Vertex v,w;
LGraph Graph;
Graph = (LGraph)malloc(sizeof(struct GNode));
Graph->Nv = VertexNum; // 初始化边
Graph->Ne = 0; // 初始化点
// 每条边的 FirstEdge 指向 NULL
for(v=0;v<Graph->Nv;v++)
Graph->G[v].FirstEdge = NULL;
return Graph;
}
// 插入一条边到邻接表的顶点指针之后
void InsertEdge(LGraph Graph,Edge E){
ptrToAdjVNode newNode;
/**************** 插入边<V1,V2> ******************/
// 为 V2 建立新的结点
newNode = (ptrToAdjVNode)malloc(sizeof(struct AdjVNode));
newNode->AdjV = E->V2;
newNode->Weight = E->Weight;
// 将 V2 插入到邻接表头
newNode->Next = Graph->G[E->V1].FirstEdge;
Graph->G[E->V1].FirstEdge = newNode;
/*************** 若为无向图,插入边<V2,V1> *************/
newNode = (ptrToAdjVNode)malloc(sizeof(struct AdjVNode));
newNode->AdjV = E->V1;
newNode->Weight = E->Weight;
newNode->Next = Graph->G[E->V2].FirstEdge;
Graph->G[E->V2].FirstEdge = newNode;
}
// 建图
LGraph BuildGraph(){
LGraph Graph;
Edge E;
Vertex V;
int Nv,i;
scanf("%d",&Nv);
Graph = create(Nv);
scanf("%d",&(Graph->Ne));
if(Graph->Ne != 0){
for(i=0;i<Graph->Ne;i++){
E = (Edge)malloc(sizeof(struct ENode));
scanf("%d %d %d",&E->V1,&E->V2,&E->Weight);
InsertEdge(Graph,E);
}
}
return Graph;
}
// 打印
void print(LGraph Graph){
Vertex v;
ptrToAdjVNode tmp;
for(v=0;v<Graph->Nv;v++){
tmp = Graph->G[v].FirstEdge;
printf("%d ",v);
while(tmp){
printf("%d ",tmp->AdjV);
tmp = tmp->Next;
}
printf("\n");
}
}
int main(){
LGraph Graph;
Graph = BuildGraph();
print(Graph);
return 0;
}
3.图的遍历
3.1深度优先搜索DFS
void DFS ( Vertex V ){
visited[ V ] = true;
for ( V 的每个邻接点 W )
if( !visited[ W ])
DFS( W );
}
3.2广度优先搜索BFS
void BFS( Vertex V ){
queue<Vertex> q;
visited[V] = true;
q.push(V);
while(!q.empty()){
V = q.front(); q.pop();
for( V 的每个邻接点 W ){
if( !visited[ W ]){
visited[W] = true;
q.push(W);
}
}
}
}
【algo&ds】6.图及其存储结构、遍历的更多相关文章
- C++编程练习(9)----“图的存储结构以及图的遍历“(邻接矩阵、深度优先遍历、广度优先遍历)
图的存储结构 1)邻接矩阵 用两个数组来表示图,一个一维数组存储图中顶点信息,一个二维数组(邻接矩阵)存储图中边或弧的信息. 2)邻接表 3)十字链表 4)邻接多重表 5)边集数组 本文只用代码实现用 ...
- 图的存储结构(邻接矩阵与邻接表)及其C++实现
一.图的定义 图是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为: G=(V,E) 其中:G表示一个图,V是图G中顶点的集合,E是图G中顶点之间边的集合. 注: 在线性表中,元素个数可以为零, ...
- 【PHP数据结构】图的存储结构
图的概念介绍得差不多了,大家可以消化消化再继续学习后面的内容.如果没有什么问题的话,我们就继续学习接下来的内容.当然,这还不是最麻烦的地方,因为今天我们只是介绍图的存储结构而已. 图的顺序存储结构:邻 ...
- 图的存储及遍历 深度遍历和广度遍历 C++代码实现
/*图的存储及遍历*/ #include<iostream> using namespace std; //----------------------------------- //邻接 ...
- K:图的存储结构
常用的图的存储结构主要有两种,一种是采用数组链表(邻接表)的方式,一种是采用邻接矩阵的方式.当然,图也可以采用十字链表或者边集数组的方式来进行表示,但由于不常用,为此,本博文不对其进行介绍. 邻接 ...
- 图的存储结构大赏------数据结构C语言(图)
图的存储结构大赏------数据结构C语言(图) 本次所讲的是常有的四种结构: 邻接矩阵 邻接表 十字链表 邻接多重表 邻接矩阵 概念 两个数组,一个表示顶点的信息,一个用来表示关联的关系. 如果是无 ...
- Hashtable数据存储结构-遍历规则,Hash类型的复杂度为啥都是O(1)-源码分析
Hashtable 是一个很常见的数据结构类型,前段时间阿里的面试官说只要搞懂了HashTable,hashMap,HashSet,treeMap,treeSet这几个数据结构,阿里的数据结构面试没问 ...
- 图的存储结构与操作--C语言实现
图(graph)是一种比树结构还要复杂的数据结构,它的术语,存储方式,遍历方式,用途都比较广,所以如果想要一次性完成所有的代码,那代码会非常长.所以,我将分两次来完成图的代码.这一次,我会完成图的五种 ...
- 图的存储与遍历C++实现
1.图的存储 设点数为n,边数为m 1.1.二维数组 方法:使用一个二维数组 adj 来存边,其中 adj[u][v] 为 1 表示存在 u到 v的边,为 0 表示不存在.如果是带边权的图,可以在 a ...
随机推荐
- Java工作流系统jflow向工作处理器传值的方法大全
关键词:工作流快速开发平台 工作流流设计 业务流程管理 asp.net 开源工作流 bpm工作流系统 java工作流主流框架 自定义工作流引擎 表单设计器 流程设计器 在启动开始节点时, ...
- Linux三剑客之awk命令详解
一.awk介绍 AWK是一种优良的文本处理工具.它不仅是 Linux 中也是任何环境中现有的功能最强大的数据处理引擎之一.这种编程及数据操作语言(其名称得自于它的创始人 Alfred Aho .Pet ...
- php5.4.16执行shell脚本
因为要用到Python脚本,所以打算直接在PHP中直接调用系统命令system(). 要注意两点: 一.PHP5.3以上不存在安全模式,即要直接执行脚本不需要作任何其他配置. 二.system函数格式 ...
- hdu 1052 Tian Ji -- The Horse Racing (田忌赛马)
Tian Ji -- The Horse Racing Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (J ...
- SpringBoot电商项目实战 — 前后端分离后的优雅部署及Nginx部署实现
在如今的SpringBoot微服务项目中,前后端分离已成为业界标准使用方式,通过使用nginx等代理方式有效的进行解耦,并且前后端分离会为以后的大型分布式架构.弹性计算架构.微服务架构.多端化服务(多 ...
- Caffe源码-Blob类
Blob类简介 Blob是caffe中的数据传递的一个基本类,网络各层的输入输出数据以及网络层中的可学习参数(learnable parameters,如卷积层的权重和偏置参数)都是Blob类型.Bl ...
- spring boot2 修改默认json解析器Jackson为fastjson
0.前言 fastjson是阿里出的,尽管近年fasjson爆出过几次严重漏洞,但是平心而论,fastjson的性能的确很有优势,尤其是大数据量时的性能优势,所以fastjson依然是我们的首选:sp ...
- git项目创建及在idea工具中使用
1.安装git管理工具 2.在自己github账号上创建一个项目仓库,比如我创建的是renrenView 网页翻译如下: 参数解析如下: 3.本地项目同步到远程仓库步骤 在本地初始化git项目 git ...
- javascript中引用传递的问题如何解决
我们有时候会向一个方法中传入一个参数,并且对这个参数做一些处理的操作: 但是因为是引用传递,处理过后会对原有的对象造成修改,无法进行反复使用. 如例子: 两次打印的结果一模一样.这样下一个方法在继续使 ...
- [UIApplication sharedApplication].keyWindow和[[UIApplication sharedApplication].delegate window]区别
参考链接:https://www.cnblogs.com/henusyj-1314/p/11643189.html 结论1.在获取到window时最好使用[[UIApplication sharedA ...