版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhangquan2015/article/details/82840120
图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为G(V,E),其中,G表示一个图,V是顶点集合
,E是边集合
。
邻接矩阵
∞是一个极限值,用65535表示,权值大多数情况是正值,个别时候是0,甚至有时候是负值,所以用∞表示不存在。
#define _CRT_SECURE_NO_WARNINGS//避免scanf不安全报错
#include "stdio.h"
#include "stdlib.h"
#include "io.h"
#include "math.h"
#include "time.h"
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXVEX 100 /* 最大顶点数,应由用户定义 */
#define INFINITY 65535
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef char VertexType; /* 顶点类型应由用户定义 */
typedef int EdgeType; /* 边上的权值类型应由用户定义 */
typedef struct
{
VertexType vexs[MAXVEX]; /* 顶点表 */
EdgeType arc[MAXVEX][MAXVEX];/* 邻接矩阵,可看作边表 */
int numNodes, numEdges; /* 图中当前的顶点数和边数 */
}MGraph;
/* 建立无向网图的邻接矩阵表示 */
void CreateMGraph(MGraph *G)
{
int i, j, k, w;
printf("输入顶点数和边数:\n");
scanf("%d,%d", &G->numNodes, &G->numEdges); /* 输入顶点数和边数 */
for (i = 0; i <G->numNodes; i++) /* 读入顶点信息,建立顶点表 */
scanf(&G->vexs[i]);
for (i = 0; i <G->numNodes; i++)
for (j = 0; j <G->numNodes; j++)
G->arc[i][j] = INFINITY; /* 邻接矩阵初始化 */
for (k = 0; k <G->numEdges; k++) /* 读入numEdges条边,建立邻接矩阵 */
{
printf("输入边(vi,vj)上的下标i,下标j和权w:\n");
scanf("%d,%d,%d", &i, &j, &w); /* 输入边(vi,vj)上的权w */
G->arc[i][j] = w;
G->arc[j][i] = G->arc[i][j]; /* 因为是无向图,矩阵对称 */
}
}
int main(void)
{
MGraph G;
CreateMGraph(&G);
system("pause");
return 0;
}
邻接表
邻接矩阵是不错的一种图存储结构,但是我们也发现,对于边数相对顶点较少的图,这种结构是存在对存储空间的极大浪费的。可以将数组与链表相结合,称为邻接表。
此时我们很容易就算出某个顶点的入度
和出度
是多少,判断两顶点是否存在弧也很容易实现。
对于带权值的网图,可以在边表结点定义中再增加一个weight的数据域,存储权值
信息即可。
#define _CRT_SECURE_NO_WARNINGS//避免scanf不安全报错
#include "stdio.h"
#include "stdlib.h"
#include "io.h"
#include "math.h"
#include "time.h"
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXVEX 100 /* 最大顶点数,应由用户定义 */
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef char VertexType; /* 顶点类型应由用户定义 */
typedef int EdgeType; /* 边上的权值类型应由用户定义 */
typedef struct EdgeNode /* 边表结点 */
{
int adjvex; /* 邻接点域,存储该顶点对应的下标 */
EdgeType info; /* 用于存储权值,对于非网图可以不需要 */
struct EdgeNode *next; /* 链域,指向下一个邻接点 */
}EdgeNode;
typedef struct VertexNode /* 顶点表结点 */
{
VertexType data; /* 顶点域,存储顶点信息 */
EdgeNode *firstedge;/* 边表头指针 */
}VertexNode, AdjList[MAXVEX];
typedef struct
{
AdjList adjList;
int numNodes, numEdges; /* 图中当前顶点数和边数 */
}GraphAdjList;
/* 建立图的邻接表结构 */
void CreateALGraph(GraphAdjList *G)
{
int i, j, k;
EdgeNode *e;
printf("输入顶点数和边数:\n");
scanf("%d,%d", &G->numNodes, &G->numEdges); /* 输入顶点数和边数 */
for (i = 0; i < G->numNodes; i++) /* 读入顶点信息,建立顶点表 */
{
scanf(&G->adjList[i].data); /* 输入顶点信息 */
G->adjList[i].firstedge = NULL; /* 将边表置为空表 */
}
for (k = 0; k < G->numEdges; k++)/* 建立边表 */
{
printf("输入边(vi,vj)上的顶点序号:\n");
scanf("%d,%d", &i, &j); /* 输入边(vi,vj)上的顶点序号 */
e = (EdgeNode *)malloc(sizeof(EdgeNode)); /* 向内存申请空间,生成边表结点 */
e->adjvex = j; /* 邻接序号为j */
e->next = G->adjList[i].firstedge; /* 将e的指针指向当前顶点上指向的结点 */
G->adjList[i].firstedge = e; /* 将当前顶点的指针指向e */
e = (EdgeNode *)malloc(sizeof(EdgeNode)); /* 向内存申请空间,生成边表结点 */
e->adjvex = i; /* 邻接序号为i */
e->next = G->adjList[j].firstedge; /* 将e的指针指向当前顶点上指向的结点 */
G->adjList[j].firstedge = e; /* 将当前顶点的指针指向e */
}
}
int main(void)
{
GraphAdjList G;
CreateALGraph(&G);
system("pause");
return 0;
}
十字链表
对于有向图
来说,邻接表是有缺陷的。关心了出度问题,想了解入度就必须要遍历整个图才能知道,反之,逆邻接表解决了入度却不了解出度的情况。可以将邻接表和逆邻接表结合,即十字链表
,对于有向图来说,十字链表是非常好的数据结构模型。
重新定义顶点的表结构:
重新定义边的表结构:
邻接多重表
对于无向图
的邻接表,关注的重点是顶点,那么邻接表是不错的选择,但如果我们更关注边的操作,比如对已访问的边做标记,删除某一条边等操作,那就意味着,需要找到这条边表结点进行操作,这其实是很麻烦的。因此,我们也仿照十字链表的方式,对边表结点的结构进行一些改造,也许就可以避免刚才提到的问题:
顶点1 | 边1 | 顶点2 | 边2 |
---|---|---|---|
ivex | ilink | jvex | jlink |
ilink
指向依附顶点ivex
的下一条边,jlink
指向依附顶点jvex
的下一条边。
这样对边的操作就方便多了,若要删除左图的(v0,v2)
这条边,只需要将右图的⑥⑨的链表指向改为^
即可。