图的存储方式
一.邻接矩阵
- 用矩阵存储边
- 用数组存储点
可以完成有向图、无向图和带权图,区别于矩阵上的数值
宏定义存储点的最大容量
#define MAX_V 10
1.创建数据结构体
typedef struct graph
{
int maxv;//存放点的最大容量
int numv;//存放点的数量
int numedges;//存放边的数量
char**edges;//存放边的矩阵结构
char *v;//存放点的数组结构
}GRAPH
2.初始化图
- 需要初始化数值,将默认最大容量进行赋值,
- 边数和点数默认为0,之后再添加时不断++
- 然后为存储边和点的矩阵和数组开辟空间,以最大的顶尖个数开辟
- 在为边开辟空间时,因为是二维数组,所以应该先开辟一个(int**)的空间,在根据最大容量遍历,为(int*)开辟空间
void init(GRAPH*g)
{
g->maxv=MAX_V;
g->numv=g->numedges=0;
//为数组开辟空间
g->v=(char*)malloc(sizeof(char)*g->maxv;
//为矩阵开辟空间
g->edges=(char**)malloc(sizeof(char*)*g->maxv);
for(int i=0;i<g->maxv;i++)
{
g->edges[i]=(char*)malloc(sizeof(char)*g->maxv);
}
//将矩阵中的数值初始为0
for(int i=0;i<g->maxv;i++)
{
for(int j=0;j<g->maxv;j++)
{
edges[i][j]=0;
}
}
}
3.展示图
- 遍历循环矩阵
4.插入顶点
调用
insert(&gm,'A');
函数
- 先判断顶点最大容量是否大于目前的顶点个数,才可以插入
- 插入时,将定点插入到最后,再将顶点个数++
g->v[g->numv++]=v;
5.插入边
调用
insertedges(&gm,'A','B');
函数
- 给出函数:获取某定点在数组中的位置,如果找不到返回-1
- 创建插入函数:通过获取两个点的位置,判断是否已经插入过,若没有,就将对应的矩阵的值赋值为1,然后将边++
6.删除顶点
- 删除数组中的顶点:找到顶点的位置,再将所有数组中的顶点向前移动
- 删除与定点相连的所有边:遍历一遍定点所在行,求出数值为1的个数,在边数中将其删除,再将矩阵中定点所在的行和列都删除掉,并且行列都往前移动一个位置
7.删除边
- 调用:
deleteedges(&gm,'A','B');
- 函数:与创建相似,先得到两个点的位置,然后判断是否找到,并且有边存在,如果有,就将矩阵中对应点的值改为0,然后边数–
8.销毁图
- 释放数组空间
- 释放矩阵空间:二维数据要从里到外一层一层释放掉它的地址
for(int i=0;i<g->numv;i++)
{
free(g->edges[i]);
}
free(g->edges);
g->edges=NULL;
- 将边数和点数重新赋值为0
9. 求邻接第一个邻接顶点
- 找到它的位置,在矩阵中找到第一个不为空的点,返回点的位置
10.求两个顶点的下一个邻接顶点
- 从第一个顶点的行和第二个顶点的列开始遍历,得到下一个数值为1的顶点,返回它的位置
11.邻接矩阵的难点和注意点:
- 难点:删除点时要删除与其相关的所有边
- 在删除和添加时记得给边数和点数增减
- 在初始化矩阵和删除矩阵时要注意其方法
- 创建一个可以找到点在数组中位置的函数会比较方便
二.邻接表:用链表存储边和点
typedef struct edge
{
int dest;
struct edge*link;
}Edge;//存储边的链表结构
typedef struct v
{
char data;//点中的数据
Edge*adj;//指向边的指针
}V;//存储点的数组结构
根据边和点创建图结构:
typedef struct graph
{
int maxv;
int numv;
int numedges;
V*NodeTable;//创建一个顶点表,实际上是一个数组结构
}Graph;
1.初始化
- 为边数、点数和最大点数赋值
- 为节点数组初始化,要乘上最大顶点个数,然后将V中的指针赋空,数值不用管他
g->NodeTable=(V*)malloc(sizeof(V)*g->maxv);
for(int i=0;i<g->maxv;i++)
{
g->NodeTable[i].adj=NULL;
}
2.循环遍历展示
- 根据数组一行一行打印,并在遍历每一行的所有链表将其打印
void show(Gragh*g)
{
edge*s;
for(int i=0;i<g->numv;i++)
{
print(%c,g->NodeTable[i].data);
s=g->NodeTable[i].adj;
while(s!=NULL)
{
print("%d",s->dest);
s=s->link;
}
print("NULL\n");
}
print("\n");
}
3.插入顶点
-判断容量是否充足,之后进行插入
4.插入边
- 创建获取顶点在数组中位置的函数,返回值为在数组中的位置,否则返回-1
- 无向图需要插入两次:首先申请边的节点空间,为其赋值,再利用链表的头插法进行插入
5.删除顶点
- 删除与该顶点相连的所有边,无向图要把其他点的边也要删除一些与它相关的
- 删除数组中该顶点:将最后一个顶点覆盖掉要删除的顶点,并将最后一个顶点所指向的指针更改为原先所指向的链表
- 找到与最后一个顶点相连的所有边并将他们存储的数值进行修改
6.删除边
- 找到两个顶点的位置,再第一个顶点的链表中找到与下标相等的节点,再将其删除掉,删除时最好用一个节点记录一下它的前驱。无向图要在两个顶点处都把边删除
7.销毁图
- 对数组遍历,删除每一个数组中的所有链表节点全部释放
- 将数组释放
- 将数值赋值为空
8.获取第一个邻接顶点
- 得到顶点位置,很容易就得到了
9.获取第二个邻接顶点
- 得到两个顶点位置,遍历链表进行查找返回下标值,没有就返回-1
10.难点和重点
- 在对边进行操作时,创建新函数得到顶点位置很有用呀
- 利用临接表时难点在构造结构体中,需要对边和顶点都重新构造一个新的结构体
- 比较麻烦的时删除顶点,需要清楚步骤
三 .邻接表和邻接矩阵的比较
- 顶点多边比较少时用邻接表,用邻接矩阵创建矩阵浪费较多
- 顶点和边都比较多用邻接矩阵
四.十字链表法
主要针对有向图
五. 临界多重表
是无向图的一种链式存储结构,解决邻接表无向图重复创建边的问题