图论基础|邻接矩阵超详细讲解(含无向/有向/带权图+完整可运行C语言代码)

邻接矩阵

文章目录

  • 邻接矩阵
    • [1. 概述](#1. 概述)
    • 2.无向图的邻接矩阵
    • 3.有向图的邻接矩阵
    • [4. 带权图的邻接矩阵](#4. 带权图的邻接矩阵)
    • [5. 邻接表的引入](#5. 邻接表的引入)
    • [6. 代码实现](#6. 代码实现)
      • [6.1 头文件部分](#6.1 头文件部分)
      • [6.2 函数实现](#6.2 函数实现)
        • [6.2.1 初始化邻接矩阵](#6.2.1 初始化邻接矩阵)
        • [6.2.2 向邻接矩阵中添加边](#6.2.2 向邻接矩阵中添加边)
        • [6.2.3 访问图中顶点](#6.2.3 访问图中顶点)
        • [6.2.4 深搜](#6.2.4 深搜)
        • [6.2.5 广搜](#6.2.5 广搜)
      • [6.3 main 函数](#6.3 main 函数)
      • [6.4 输出结果](#6.4 输出结果)

1. 概述

  • 图的邻接矩阵是⽤两个数组来表示,⼀个⼀位数组存储图中的顶点信息,⼀个⼆维数组(我们将这个数组称之为邻接矩阵)存储图中的边的信息

2.无向图的邻接矩阵

  • 我们可以设置两个数组,顶点数组为 vertex 4 = { V0 , V1 , V2 , V3 },边数组arc4 4为对称矩阵(0表示不存在顶点间的边,1表示顶点间存在边)
  • 对称矩阵

所谓对称矩阵就是n阶矩阵的元素满⾜a i j = a j i (0 <= i , j <= n) ,即从矩阵的左上⻆到右下⻆的主对⻆线为轴,右上⻆的元与左下⻆相对应的元全都是相等的

其实 普通无向图的邻接矩阵一定是对称矩阵

  • 又画的另外一个例子
  • 有了这个⼆维数组组成的对称矩阵,我们就可以很容易地知道图中的信息:

判定任意两顶点是否有边⽆边

可以轻松知道某个顶点的度,其实就是这个顶点 Vi 在邻接矩阵中第 i ⾏(或第 i 列)的元素之和

求顶点Vi的所有邻接点就是将矩阵中第i⾏元素扫描⼀遍,arc i j 为1就是邻接点

3.有向图的邻接矩阵

  • ⽆向图的边构成了⼀个对称矩阵,貌似浪费了⼀半的空间,那如果是有向图来存放,会不会把资源都利⽤得很好呢?
  • 可⻅顶点数组 vertex 4 = { V0 , V1 , V2 , V3 },弧数组arc 4 4 也是⼀个矩阵,但因为是有向图,所以这个矩阵并不对称,例如由V0到V3有弧,得到arc0 3=1,⽽V3到V0没有弧,因此arc3 0=0
  • 另外有向图是有讲究的,要考虑⼊度和出度,顶点V1的⼊度为1,正好是第V1列的各数之和,顶点V1的出度为2,正好是第V1⾏的各数之和

4. 带权图的邻接矩阵

  • 带权图中的每⼀条边上带有权值,邻接矩阵中的值则为权值,当两个顶点之间没有弧时,则⽤⽆穷⼤表示

这⾥"∞"表示⼀个计算机允许的、⼤于所有边上权值的值

5. 邻接表的引入

  • 这个时候我们会发现⼀个问题,就是空间浪费问题,尤其是⾯对边数相对⽐较少的稀疏图来说,这种结构⽆疑是存在对存储空间的极⼤浪费

  • 因此我们可以考虑另外⼀种存储结构⽅式,例如把数组与链表结合在⼀起来存储,这种⽅式在图结构也适⽤,我们称为邻接表(Adjacency List)

    这个我们将下一节来写,现在去完成代码部分

6. 代码实现

6.1 头文件部分

c 复制代码
#define MaxNodeNum 20
#define INF        1E4   

//邻接矩阵的顶点结构 表示一个顶点的信息
typedef struct 
{
	int no;          //顶点编号
	char* show;      //显示行为
} MatrixVertex;

typedef int MatrixEdge;    //边里面存的内容

//图邻接矩阵表示法
typedef struct 
{
	MatrixVertex vex[MaxNodeNum];              //存储顶点信息,使用一维数组存储顶点
	int nodeNum;                               //约束实际顶点数量
	MatrixEdge edges[MaxNodeNum][MaxNodeNum];  //边的集合    //存储边的结构,矩阵
	int edgeNum;   //边的数量
	int directed;  //是否有向      
}MGraph;
           
/* 1.初始化邻接矩阵  
*    graph     :哪个图
*    names[]   : 顶点名称
*	 num       :顶点数量
*	 directed  :是否有向
*	 edgeValue :用来初始化边 0 ? ∞
*/
void initMGraph(MGraph* graph,  char* names[], int num, int directed, int edgeValue);

/*
* 2.向邻接矩阵中添加边
* graph:  向哪个图添加边
* x    :  起始顶点的编号
* y    : 终点顶点的编号
* w    : 边的权值
*/
void addMGraphEdge(MGraph* graph, int x, int y, int w);

//3.访问图中顶点
void visitMGraphNode(MatrixVertex *node);
void initVisited();

//4.深搜                   /*v是节点的下标*/
void DFS_MGraph(MGraph* graph, int v);
//5.广搜
void BFS_MGraph(MGraph* graph, int v);

6.2 函数实现

6.2.1 初始化邻接矩阵
c 复制代码
void initMGraph(MGraph* graph,  char* names[], int num, int directed, int edgeValue) 
{
    //初始化 是否有向 顶点数量 边的数量
	graph->directed = directed;
	graph->nodeNum = num;
	graph->edgeNum = 0;
	//初始化 顶点集合 边的集合
	memset(graph->vex, 0, sizeof(graph->vex));
	memset(graph->edges, 0, sizeof(MatrixEdge) * MaxNodeNum * MaxNodeNum);
	//初始化顶点编号 显示行为 边
	for (int i = 0;i < num;i++) 
    {
		graph->vex[i].no = i;
		graph->vex[i].show = names[i];
		for (int j = 0;j < num;j++) 
        {
			graph->edges[i][j] = edgeValue;
		}
	}
}
6.2.2 向邻接矩阵中添加边
c 复制代码
//判断两个点之间有没有边
static int isEdge(int weight) 
{
	if (weight > 0 && weight < INF) 
    {
		return 1;
	}
	return 0;
}

void addMGraphEdge(MGraph* graph, int x, int y, int w) 
{
    //越界情况
	if (x<0 || x>graph->nodeNum)
    {
		return;
	}
	if (y<0 || y>graph->nodeNum)
    {
		return;
	}
    //没边加边
	if (!isEdge(graph->edges[x][y])) 
    {  
		graph->edges[x][y] = w;             
		if (graph->directed == 0) 
        {   //无向图  对称的 (加对称的边)
			graph->edges[y][x] = w;
		}
		graph->edgeNum++;
	}
}
6.2.3 访问图中顶点
c 复制代码
void visitMGraphNode(MatrixVertex* node) 
{
	printf("\t%s", node->show);
}

static int MGraphVisited[MaxNodeNum];
void initVisited()
{   //因为这个MGraphVisited数组同时用于深搜和广搜
	memset(MGraphVisited, 0, sizeof(MGraphVisited));
}
6.2.4 深搜
c 复制代码
void DFS_MGraph(MGraph* graph, int v) 
{//深搜
	visitMGraphNode(&graph->vex[v]);
	MGraphVisited[v] = 1;                   //访问过则标记一下
	for (int i = 0;i < graph->nodeNum;i++) 
    {//有边且未访问过
		if (isEdge(graph->edges[v][i] && MGraphVisited[i] == 0)) 
        {
			DFS_MGraph(graph, i);
		}
	}
}
6.2.5 广搜
c 复制代码
void BFS_MGraph(MGraph* graph, int v)
{//广搜
    //用队列 手写个简单的队列
	int que[MaxNodeNum];
	int rear = 0, front = 0; int cur;
	//初始化该队列
	rear = (rear + 1) % MaxNodeNum;
	que[rear] = v;
	MGraphVisited[v] = 1;
    
    //队列中有内容就一直出队
	while (front != rear) 
    {
		//出队
		front = (front + 1) % MaxNodeNum;
		cur = que[front];

		visitMGraphNode(&graph->vex[cur]);
		for (int i = 0;i < graph->nodeNum;++i) 
        {//不断的放节点   入队
            //有边且未访问过
			if (isEdge(graph->edges[cur][i] && MGraphVisited[i] == 0)) 
            {
				rear = (rear + 1) % MaxNodeNum;
				que[rear] = i;
				MGraphVisited[i] = 1;
			}
		}
	}
}

6.3 main 函数

c 复制代码
void testMatruxGraph(MGraph *grap) 
{
	char* nodeName[] = { "v1","v2", "v3", "v4", "v5", "v6", "v7", "v8" };
    // 无向图
	initMGraph(grap, nodeName, sizeof(nodeName) / sizeof(nodeName[0]), 0, 0);
	addMGraphEdge(grap, 0, 1, 1);  
	addMGraphEdge(grap, 0, 2, 1);     
	addMGraphEdge(grap, 1, 3, 1);
	addMGraphEdge(grap, 1, 4, 1);
	addMGraphEdge(grap, 2, 5, 1);
	addMGraphEdge(grap, 2, 6, 1);
	addMGraphEdge(grap, 3, 7, 1);
	addMGraphEdge(grap, 4, 7, 1);
	addMGraphEdge(grap, 5, 6, 1);
}

void test01() 
{
	MGraph g1;
	testMatruxGraph(&g1);
	printf("graph have %d edge!\n", g1.edgeNum);
	//DFS
	DFS_MGraph(&g1, 0);  //从v1开始访问
	initVisited();
	printf("\n");
	//BFS
	BFS_MGraph(&g1, 0);
}

int main()
{
    test01()
    return 0;
}

6.4 输出结果

c 复制代码
graph have 9 edge!
        v1      v2      v4      v8      v5      v3      v6      v7
        v1      v2      v3      v4      v5      v6      v7      v8
  • 测试案例可参考该图

嘻嘻嘻嘻 邻接矩阵部分到此结束😆😆

邻接表 静候更新 👻👻👻😸😸😸

(有错误欢迎指出) (疑问也是)❤️❤️😍😍💖💖

持续更新中...

相关推荐
布莱克6051 小时前
数据库索引分类:数据结构、物理存储与逻辑角度详解
数据结构·数据库
hanhahai2 小时前
指针与函数(函数指针与指针函数)
算法
ValhallaCoder2 小时前
Leetcode-hot100(2026.08.17)
python·算法·leetcode
泡沫冰@2 小时前
GO 语言基础
开发语言·算法·golang
FakeOccupational3 小时前
【电路笔记 STM32】stm32f103 实现HAL_UART_Transmit_DMA + 普通模式/连续模式的 UART DMA 收发
笔记·stm32·嵌入式硬件
bkspiderx3 小时前
从零开始:VS Code搭建C/C++开发环境全指南(Windows/macOS/Linux)
c语言·c++·windows·vs code·c/c++开发环境
wuyk5553 小时前
第 6 章 FOC 完整系统整合:从算法到真机跑起来
c语言·开发语言·stm32·单片机·嵌入式硬件
luj_17683 小时前
元设计的诱惑与现实
c语言·开发语言·c++·经验分享·算法
小星星闪亮登场3 小时前
ST表--倍增思想
开发语言·数据结构·c++·算法·思维