数据结构与算法--图的存储方式
1. 图的定义
- 定义:图是由顶点的有穷非空结合和顶点之间的集合组成,数据元素(顶点)之间具有任意关系,图中任意两个数据元素之间都可能相关
- 各种图:
无向图(顶点到顶点之间的边没有方向,则称这条边为无向边)
有向图(若从顶点 到顶点 的边有方向,则称这条边为有向边,也称为弧)
顶点A
到顶点D
的边就是弧,用<A, D>
表示弧,注意不能写成<D, A>
上图可以表示为G= (V2,{E2})
,其中顶点集合V2={A,E,C,D}
, 弧集合E2={<A,D>,<A,E>,<A,C>,<E,D>}
。
无向完全图
有向完全图
有些图的边
或弧
具有与它相关的数字,这种与图的边或弧相关的数叫做权
。这些权可以表示从一个顶点到另一个顶点的距离或耗费。
这种带权
的图通常称为网
。如下图就是一张带权的图,即标识中国四大城市的直线距离的网,此图中的权就是两地的距离。
广度优先搜索算法:数据结构是队列。通过将顶点存入队列中,最先入队列的顶点先被探索。
深度优先搜索算法:数据结构是栈。通过将顶点存入栈中,沿着路径探索顶点,存在新的相邻顶点就去访问
2. 图的存储
2.1. 图的存储介绍
图用 邻接矩阵存储方式 来存储
-
一个
一维数组
存储图中顶点信息
, -
一个
二维数组(称为邻接矩阵
)存储图中的边或弧
的信息。
设图G
有n
个顶点,则邻接矩阵是一个nxn
的方阵,定义为:
如下无向图:
如下有向图
我们知道,每条边上带有权值
的图
叫做网
,那么在存储的时候,可以采用权值代替矩阵中的0、1
,权值不存在的元素之间用∞
表示,如下图,左图是一个有向网图,右图就是它的邻接矩阵。
2.2. 邻接矩阵
定义一个数组,存储顶点,定义一个邻接矩阵(二维数组)
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXVEX 100 // 最大顶点数
#define INFINITYC 0
typedef int Status;
typedef char VertexType; // 顶点类型
typedef int EdgeType; // 边的权值的类型
// 定义图的结构
typedef struct {
VertexType vers[MAXVEX]; // 顶点表
EdgeType arc[MAXVEX][MAXVEX];// 邻接矩阵
int numNodes; // 顶点数
int numEdges; // 边数
}MGraph;
图的存储方法:
void CreateMGraph(MGraph *G){
int i, j, k, m;
// 1.输入顶点数
printf("输入顶点数:");
scanf("%d", &G->numNodes);
// 2.输入边数
printf("输入边数:");
scanf("%d", &G->numEdges);
printf("顶点数:%d,边数:%d\n",G->numNodes,G->numEdges);
// 3.输入顶点表
printf("输入顶点:");
for (i = 0; i <= G->numNodes; i++) {
scanf("%c", &G->vers[i]);
}
// 4.初始化邻接矩阵
for (i = 0; i < G->numNodes; i++) {
for (j = 0; j < G->numNodes; j++) {
G->arc[i][j] = INFINITY;
}
}
// 5.输入边信息
for (k = 0; k < G->numEdges; k++) {
printf("输入边(vi,vj)上的下标i,下标j,权w(逗号分隔)\n");
scanf("%d,%d,%d", &i, &j, &m);
G->arc[i][j] = m;
//如果无向图,矩阵对称;
//G->arc[j][i] = G->arc[i][j];
}
// 6.打印
for (int i = 0; i < G->numNodes; i++) {
printf("\n");
for (int j = 0; j < G->numNodes; j++) {
printf("%d ",G->arc[i][j]);
}
}
printf("\n");
}
2.3. 邻接表
#define M 100 //顶点最大个数
#define TRUE 1
#define FALSE 0
typedef char Element;
// 定义邻接表节点
typedef struct Node {
int adj_vex_index; // 弧头下标
Element data; // 权重
struct Node *next; // 边指针
}EdgeNode;
// 定义顶点表结构
typedef struct vNode {
Element data; // 权重
EdgeNode * firstedge; // 顶点的下一个
}VertexNode,Adjlist[M];
// 定义图结构
typedef struct Graph{
Adjlist adjlist; // 顶点表
int arc_num; // 边数
int node_num; // 顶点数
int is_directed; // 是否是有向图
}Graph, *GraphLink;
图的存储(邻接表)
void creatGraph(GraphLink *g){
int i, j, k;
EdgeNode *p;
// 输入边数,顶点数,是否有向
printf("输入顶点数目,边数和有向?:\n");
scanf("%d %d %d", &(*g)->node_num, &(*g)->arc_num, &(*g)->is_directed);
// 输入顶点信息
for (i = 0; i < (*g)->node_num; i++) {
getchar();
scanf("%c", &(*g)->adjlist[i].data);
(*g)->adjlist[i].firstedge = NULL;
}
printf("输入边信息:\n");
for (k = 0; k < (*g)->arc_num; k++) {
getchar();
scanf("%d %d", &i, &j);
// 创建新节点
p = (EdgeNode *)malloc(sizeof(EdgeNode));
// 弧头的下标
p->adj_vex_index = j;
// 头插法
p->next = (*g)->adjlist[i].firstedge;
// 将顶点数组[i].firstedge 设置为p
(*g)->adjlist[i].firstedge = p;
// 判断是否是有向
if (!(*g)->is_directed) {
// 创建新节点
p = (EdgeNode *)malloc(sizeof(EdgeNode));
// 弧头的下标
p->adj_vex_index = i;
// 头插法
p->next = (*g)->adjlist[j].firstedge;
// 将顶点数组[i].firstedge 设置为p
(*g)->adjlist[j].firstedge = p;
}
}
}
void putGraph(GraphLink g){
int i;
printf("邻接表中存储信息:\n");
//遍历一遍顶点座标,每个再进去走一次
for (i = 0; i < g->node_num; i++) {
EdgeNode * p = g->adjlist[i].firstedge;
while (p) {
printf("%c->%c ", g->adjlist[i].data, g->adjlist[p->adj_vex_index].data);
p = p->next;
}
printf("\n");
}
}