数据结构与算法——图的邻接表表示法代码

一、图的存储结构

人类对图的操作常常是有直观的平面图,比如地图上的相关的点、线。

而C/C++编码时是是无法识别相关的图的,这需要我们用特别的逻辑与存储结构结构(邻接表、邻接矩阵)来实现建图的的操作,那我们试着用邻接表、邻接矩阵对下图进行表示吧!
在这里插入图片描述点和边的英语
点和边的英语如图所示,方便大家阅读代码
采取不同的存储结构对图的相关运算操作的时间、空间复杂度有较大的影响,我们待会分析一下。

1. 邻接矩阵

1.邻接矩阵的定义

顾名思义,此方法就是采用矩阵记录点之间的对应关系。
结点数为n的图G需要用n × n的矩阵,若将G的各点用 v1,v2,v3…来表示,则有以下两种情况:
1.无权图:在这里插入图片描述2.带权图
在这里插入图片描述

代码如下:

#define MAX_SIZE 1024
typedef char VextexType;//点的名称
typedef int EdgeType;//带权边的权值
typedef struct 
{
	VertexType Vex[MAX_SIZE];//顶点表
	EdgeType Edge[MAX_SIZE][MAX_SIZE];//边表,也就是邻接矩阵
	int vexnum,arcnum;//顶点数和弧数
}MGraph;

如下图:
口诀:入列出行
对第 j 行:本行累计和表示结点 Aj 的出度(A j都指向谁)
对第 i 列:本列累计和表示结点 Ai 的入度(都谁指向A i)

若是无向图没有指向关系,那么该矩阵一定是对称的,可以采用矩阵的压缩,参看我往期的文章。
在这里插入图片描述

2. 邻接表

1.邻接表的定义

采用数组表示所有的顶点,但是每个顶点作为链表的头节点,此链表表示所有依附于顶点i的边。
在这里插入图片描述在这里插入图片描述

#define MAX_SIZE 1024
typedef struct ArcNode  //对边的定义
{ 
int adjvex;//边指向的点
struct ArcNode *next;//以同一起点的下一条边
int weight;//边权重
}ArcNode;
typedef struct VNode//对点的定义
{
    VertexType data;//顶点的名称
    ArcNode* first;//点的第一条边
}VNode,AdjList[MAX_SIZE];
typedef struct {
AdjList vertices;邻接表
int vexnum,arcnum;
}ALGraph;

相信大家可能有点懵,那么我们来讨论一下
Q:边的结构里面的next呢,顶点的结构的first是指什么?
A:这是因为邻接表就是N行链表,每一行都相当于一个链表,N为顶点数量,链表头节点就是链表中的每一个顶点,first是指从该点出发的某一条边,next是指由该点出发的边 的另一条也从该顶点出发的边

Q:边表也就是链表,next指向的各边有没有先后顺序?
A:next的指向没有先后顺序,所以邻接表不唯一
tips:我们引入next的原因是在进行图的遍历时,能够遍历到所有结点,以及求单源最短距离时,当通过此边找不到时可以回溯到顶点,选择同顶点的下一条边进行遍历找最优解。
下图右边的邻接表:绿色的是顶点,黄色的是边,边里面的字符表示该边临接的顶点,也就是next在这里插入图片描述

2.代码实现

< 1初始化邻接表
void Init_pic(ALGraph* G)
{
    G->vertices = new AdjList[Maxsize];//开辟空间
    G->arcnum = 0;//边数为0
    G->vexnum = 0;//点数为0
}
< 2 进行命名转化操作

引入这一步骤的目的是我们的顶点都是有名字的 通常是char类型的,我们的数组里都是数字如a[n],还要进行转化操作
通过顶点对应的字符寻找顶点在图中的顶点,也就是邻接表的第一列
找到返回编号,找不到返回 -1

int Location(ALGraph* G, char c)//通过顶点对应的字符寻找顶点在图中的邻接点
{
	for (int i = 0; i < G->vex; i++)
	{
		if (G->vertices[i].data == c)
		{
			return i;
		}
	}
	return -1;
}
< 3 点线关联

注意我这里向链表插入同顶点的边时使用的是前插法
读者也可以使用后插法,改变的只是搜索顺序
在这里插入图片描述

void Create(ALGraph* G)
{
    cout << "请输入该图的顶点数以及边数" << endl;
    cin >> G->vexnum >> G->arcnum;
    cout << "请输入相关节点" << endl;
    for (int i = 0; i < G->vexnum; i++)
    {
        cin >> G->vertices[i].data;//不断输入顶点的字符(名称)
        G->vertices[i].first = NULL;
    }
    char v1 = 0, v2 = 0;//保存输入节点的字符
    int i1, i2;//保存顶点在数组的下标,i1是起始点
    int weight = 0;//边的权重初始化为0
    cout << "请输入有关联边的两个顶点和边的权重,按指向的顺序输入" << endl;
    for (int i = 0; i < G->arcnum; i++)
    {
        cin >> v1 >> v2>>weight;
        i1 = Location(G, v1);//定位到下标,字符转数字
        i2 = Location(G, v2);
        if (i1!=-1&&i2!=-1)//同时存在这两点时
        {
            ArcNode* tmp = new ArcNode;
            tmp->adjvex = i2;  //别忘了adjvex是该边指向的顶点序号
            tmp->next = G->vertices[i1].first;//前插法
            tmp->weight = weight;
            G->vertices[i1].first = tmp;
        }
    }
}
< 4 输入并构建图

关于文章开始的那个图我们可以这样输入
在这里插入图片描述

3. 十字链表

4. 邻接多重表

二、图的遍历

图的遍历算法是指从图中的某一顶点出发,按照某种搜索方法沿着图中边对图中所有结点进行访问

1. 广度优先搜索

< 1 算法分析

< 2 BFS求单源最短路径问题

< 3 广度优先生成树

2. 深度优先搜索

< 1 算法分析

< 2 BFS求单源最短路径问题

< 3 广度优先生成树

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值