图论算法篇:邻接矩阵以及邻接表和链式前向星建图

在这里插入图片描述

那么我们从这一篇文章开始就正式进入了图相关算法的学习,那么对于认识图的各种算法之前,那么我们首先得学会建图,但是要在建图之前,我们又得对图这种非常基本非常常见的数据结构有着一定的认识,所以我们就先来简单回顾一下我们图这个数据结构

那么对于我们的图这个数据结构来说,它是一个多对多的非线性的数据结构,那么图可以看成由两部分组成,分别是节点以及边组成,我们首先可以根据我们的边是否有向,可以将我们一个图分成有向图与无向图。其中无向图我们可以理解为双向图,也就是假设节点a与节点b有一条边,那么我们可以认为节点a到节点b有一条有向边,同时节点b到节点a也有一条有向边

在这里插入图片描述

我们还可以将图按照边是否带权值分为带权图与不带权图,那么这个权值的具体含义可以是我们地图上各个城市作为节点,那么他们之间的连线的权值就是距离或者说从a城市到达b城市的油量消耗,那么对于我们的图还可以按照节点之间边的连线的数量分为稀疏图和稠密图等等其他各种分类,那么这里我们只需要掌握最常见也就是最关键的两种分类的图即可,也就是有向无向图和带权不带权图

那么回顾了我们的图这个数据结构,那么我们接下来就得知道怎么表示图这个数据结构,我们知道对于线性结构我们可以用数组或者链表来实现线性表,而对于树型结构,那么我们可以用类或者结构体来描述我们树中的一个节点,那么在这个节点中保存了其子节点的指针以及这个节点本身的值,然后通过一个根节点往下遍历,那么这里我们的图有如何来表示呢,那么我们建图就有三种方式,那么就如我们的标题所说,分别是邻接矩阵以及邻接表和链式前向星,那么我会在下文分别介绍这三种建图方式

1.邻接矩阵

那么邻接矩阵的建图方式那就是我们定义一个二维的数组,那么假设我们的节点个数是n个,那么我们就定义n*n的二维数组,那么其中这个二维数组的行下标与列下标都对应着相应的节点标号,那么这里我们有了二维数组,那么我们怎么表示图中各个编号的节点的连接的信息呢?

有向图建立:

那么我们首先初始化我们的二维数组的各个元素为0或者-1,那么如果我们节点a与节点b有一条a到b的有向边,假设a节点对应的编号是0,b节点对应的编号是1,那么我们就在二维数组的第0行第1列(这里第0行可能会引起误会,因为我这里习惯用下标称呼二维数组的行与列,这里的第0行就是数组的第一行)位置处设置为1,那么只要我们a到b有一条边,那么我们就在a所对应的节点编号就作为我们的行下标,b节点编号就作为列下标,那么对应位置处设置为1即可,那么在这个二维数组中元素值为1就代表该位置处对应的行下标所对应的节点到列下标所对应的节点有一条有向边

[1, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 0, 0]

代码实现:

// 添加边操作
void addEdge(int u, int v, vector<vector<int>>& graph) {
    graph[u][v] = 1; 
}

无向图建立:

只要我们会了有向图建立,那么无向图建立就十分简单,那么我们上文说过我们的无向图可以视作一个有向图,那么a和b节点有连接,那么我们就认为a和b节点有两条边,分别是a到b方向有一条边和b到a方向上有一条边,所以我们可以按照我们有向图的思路去建图,那么假设这里a和b连接,并且a节点编号是0,b是1的话,那么就在数组(0,1)和(1,0)位置处设置为1

[1, 0, 0, 0]
[1, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 0, 0]

带权图建立:

那么带权图的话,我们就在我们有向图与无向图的基础上,我们这里数组的元素值0和1表示的是存在或者不存在,那么如果我们表示权值的话,那么我们在设置数组的元素值的时候,我们就不设置为1了,而是直接设置为权值,比如假设节点a的编号为0,节点b的编号为1,有一条a到b且权值为5的有向边,那么这里我们就在数组的(0,1)设置为5即可,那么这里的数组的元素值-1就表示不存在,因为边的权值一般不为负数,如果a和b有一个权值为k的边,那么我们就把相应位置的值设置为k即可

[k, -1, -1, -1]
[-1, -1, -1, -1]
[-1, -1, -1, -1]
[-1, -1, -1, -1]

代码实现:

  void addWeightedEdge(vector<vector<int>>& graph ,int u, int v, int k) {
      graph[u][v] = k; 
}

那么我们知道了如何用邻接矩阵的方式来建图之后,那么我们再来说一下我们邻接矩阵建图的一个优点与缺点,那么我们知道由于我们的邻接矩阵的实现是采取数组的方式来实现的,而数组有着随机访问的特性,所以对于查询(i,m)位置的元素值能做到时间复杂度是O(1),那么意味着我们查询我们这个数组两个节点是否有边是十分高效的,但是邻接矩阵的缺点也很明显,那么如果我们该图的节点个数变多,那么我们这里开辟的数组的大小就会很大,空间复杂度就会很高,并且如果我们该图是稀疏图的话,那么我们很多节点之间没有连线,那么我们邻接矩阵其实有很大一部分空间是被浪费的,因为我们建图更希望得到有边的信息,而这里邻接矩阵的实现则还表示了一些节点没有边的信息,所以对于我本人来说,我是不喜欢也不推荐用邻接矩阵这个方式来建图,我们邻接矩阵的应用场景一般是稠密图并且节点个数还要少.

2.邻接表

那么邻接表就在邻接矩阵的基础上优化了一些邻接矩阵的缺点,我们知道我们邻接矩阵有n个节点要开辟n*n的空间,那么对于一些没有边的信息,我们这里邻接矩阵也表示了,所以我们这里的邻接表就只表示有边的节点的信息,大大优化了存储的空间

那么接下来我来说一下邻接表的原理,那么我们邻接表是用一个指针数组,那么每一个元素是一个指针,其中就指向一个链表,我们一个图有n个节点,那么我们就开辟长度为n的指针数组,当然这个数组的每个位置的下标就对应图中的节点编号,那么这里我们每一个数组元素指向的一个链表就表示与该节点直接相连的节点

但是实际上我们实现我们的邻接表的时候,这里其实不用一个指针数组那么麻烦,这里可以用一个动态的二维数组来实现,那么我们还是开辟一个长度为n的数组,数组的下标对应节点编号。

假设有4个节点(a, b, c, d),编号分别为0, 1, 2, 3,并且有以下边:

a -> b
a -> c
a -> d
b -> c
邻接表表示:

adj[0](节点a):[1, 2, 3](节点b, c, d)
adj[1](节点b):[2](节点c)
adj[2](节点c):[](无边)
adj[3](节点d):[](无边)

那么这里我们就将我们这里与节点a相连的b,c,d的节点编号压入进我们数组下标为0的一个一维数组当中,那么这里同理如果与节点编号为1的直接相连的节点编号有2

那么我们就把与节点编号相连的数组直接压入该下表为1的一维数组中去

那么这里我们的动态的二维数组其实就是模拟链表的一个逻辑结构,这里我们的编号就相当于是指针,因为我们的编号对应数组的下标,那么我们数组由于数随机访问的特性,那么我们可以利用编号来访问到各个节

那么对于有向图来说比如a到b有一条有向边,那么我们就在a节点对应的数组中将b的节点编号压入a对应的一维数组中,而如果是无向图,a和b之间有一条边,那么我们就把b的编号压入a对应的一维数组中,同时也把a的编号压入b的数组中

代码实现:

vector<vector<int>> adj (N);
//无向图
void addEdge(int u, int v) {
    adj[u].push_back(v);
    adj[v].push_back(u);
}
//有向图
void addEdge(int u, int v) {
    adj[u].push_back(v);
}

而对于带权图的话,这里我们的二维动态数组的元素就不再是一个int类型表示节点编号,那么就应该是一个结构体类型或者pair类型来存储了连接的边与对应的权值

代码实现:

vector<vector<pair<int,int>>> adj (N);
void addEdge(int u, int v,int weight) {
    adj[u].push_back({v,weight});
    adj[v].push_back({u,weight});
}

那么对于我们的邻接表来说,这里优=我们只存储了边的信息,那么这就大大优化了空间,并且遍历也十分方便,那么他是一个优秀的一个建图方式,那么我们对于有n个节点,m条边的图来说,我们这里的邻接表的空间复杂度就是o(n+m)

3.链式前向星

那么链式前向星是我比较推荐的一个建图方式,那么在比赛以及面试的时候都十分有用的一个建图方式

那么链式前向星的一个原理就是准备三个一维动态数组,分别是head以及next以及to数组,那么我们该图如果有n个节点,那么我们就准备n长度的head数组,那么数组的下标就对应着节点的编号,然后我们会为我们的每一条边分配一个编号,那么该编号同理对应着我们next数组的下标,而to数组则是我们这条边所连接的一个节点

  • 核心组件:
    • head[]: 每个节点对应的链表的头节点索引。初始化为-1,表示该节点没有边。
    • edge[]: 存储边的“下一个”边索引,形成链表结构。
    • to[]: 存储边连接的目标节点索引。
    • cnt: 边计数器
    • weight[](可选):存储边的权重,对于带权图是必要的

那么我来举一个例子来说明,假设有4个节点(a, b, c, d),编号分别为0, 1, 2, 3,并且有以下边:

a -> b
b -> c
a -> d
b -> d

初始化:

head = [-1, -1, -1, -1]
next = [](动态扩展)
to = [](动态扩展)
weight = [](如果需要权重)
cnt = 0
添加边:

a -> b:

cnt = 0
next[0] = head[0] = -1
head[0] = 0
to[0] = 1(b的编号)
cnt = 1
b -> c:

cnt = 1
next[1] = head[1] = -1
head[1] = 1
to[1] = 2(c的编号)
cnt = 2
a -> d:

cnt = 2
next[2] = head[0] = 0(a之前的头边)
head[0] = 2
to[2] = 3(d的编号)
cnt = 3
b -> d:

cnt = 3
next[3] = head[1] = 1(b之前的头边)
head[1] = 3
to[3] = 3(d的编号)
cnt = 4
最终结构:

head = [2, 3, -1, -1]
next = [-1, 0, -1, 1]
to = [1, 2, 3, 3]

所以我们链式前向星对于每一个节点与它直接相连的边,按照该流程的处理逻辑,也就是我们让该该节点相连的边的编号在next数组中对应的下标位置处保存head对应该节点位置处的值,然后让to数组对应该边的编号保存对应连接的节点,那么这就好比模拟的是一个链表的头插法的一个逻辑来实现的

那么对于带权的图我们采取链式前向星我们就可以在额外开一个一维数组weight,那么weight数组的每一个位置下标对应节点编号,其中元素值就表示该边的权值

代码实现:

void add(vector<int>& head,vector<int>& next,vector<int>& to,int u,int v,int cnt)
{
//u到v有一条有向边
   next[cnt]=head[u];
   head[u]=cnt;
   to[cnt]=v;
}
//遍历我们该节点编号为u直接相连的节点
int ant=head[u];
while(ant!=-1)
{
cout<<"u连接的节点是"<<to[ant];
ant=next[cnt];
}

4.结语

那么这就是本篇文章介绍我们常见的三种建图的方式,那么我们一定要根据图的性质选择合适的建图方法,但是在这三种建图方式中,其实我最不推荐的就是我们第一种邻接矩阵的建图方式,那么在学习完我们的建图之后,那么我们便正式进入了我们图相关的算法学习,那么后面会更新图相关的算法比如拓扑排序以及bfs和迪杰斯特拉算法,那么我会持续更新,希望你多多关注与支持,那么如果本篇文章对你有帮助的话,请多多三连加关注哦。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/970871.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

内容中台如何搭建?

内容概要 企业搭建内容中台的核心目标在于通过技术驱动的内容资产整合与流程优化&#xff0c;实现跨业务场景的内容高效复用与敏捷响应。这一过程始于对业务需求的深度拆解&#xff0c;包括明确内容生产、分发、管理的核心痛点&#xff0c;例如多部门协作效率低下、内容版本混…

Navicate数据库连接工具的下载与安装,附带使用(连接MySQL,建表、增删改查)

1.Navicate安装包下载 Navicat 中国 | 支持 MySQL、Redis、MariaDB、MongoDB、SQL Server、SQLite、Oracle 和 PostgreSQL 的数据库管理 2.安装 3.连接数据库 4.建表和四个基本的增删改查语句 CREATE DATABASE ckk_school20250216;USE ckk_school20250216;CREATE TABLE stude…

探秘 Map 和 Set 底层:二叉搜索树与哈希表的深度解析,解锁高效数据存储秘密!

目录 二叉搜索树&#xff08;红黑树&#xff09; 概念&#xff1a; 示例&#xff1a; Java代码实现&#xff1a; 性能分析&#xff1a; 哈希表 概念&#xff1a; 哈希冲突&#xff1a; 哈希冲突的避免&#xff1a; 避免方式1 -- 哈希函数设计 避免方式2 -- 负载因子…

python从入门到进去

python从入门到进去 第一章、软件和工具的安装一、安装 python 解释器二、安装 pycharm 第二章、初识 python一、注释可分三种二、打印输入语句三、变量1、基本数据类型1.1、整数数据类型 int1.2、浮点数数据类型 float1.3、布尔数据类型 boolean1.4、字符串数据类型 string 2、…

001-监控你的文件-FSWatch-C++开源库108杰

fswatch 原理与应用简介fswatch 安装fswatch 实践应用具体应用场景与细节补充 1. 简介 有些知识&#xff0c;你知道了不算厉害&#xff0c;但你要是不知道&#xff0c;就容易出乱。 很多时候&#xff0c;程序需要及时获取磁盘上某个文件对象&#xff08;文件夹、文件&#xff0…

华为云kubernetes基于keda自动伸缩deployment副本(监听redis队列长度)

1 概述 KEDA&#xff08;Kubernetes-based Event-Driven Autoscaler&#xff0c;网址是https://keda.sh&#xff09;是在 Kubernetes 中事件驱动的弹性伸缩器&#xff0c;功能非常强大。不仅支持根据基础的CPU和内存指标进行伸缩&#xff0c;还支持根据各种消息队列中的长度、…

解锁机器学习核心算法 | 决策树:机器学习中高效分类的利器

引言 前面几篇文章我们学习了机器学习的核心算法线性回归和逻辑回归。这篇文章我们继续学习机器学习的经典算法——决策树&#xff08;Decision Tree&#xff09; 一、决策树算法简介 决策树算法是一种典型的分类方法&#xff0c;也是一种逼近离散函数值的方法。它的核心思想…

CRISPR spacers数据库;CRT和PILER-CR用于MAGs的spacers搜索

iPHoP&#xff1a;病毒宿主预测-CSDN博客 之前介绍了这个方法来预测病毒宿主&#xff0c;今天来介绍另一种比较用的多的方法CRISPR比对 CRISPR spacers数据库 Dash 在这可以下载作者搜集的spacers用于后期比对 CRT和PILER-CR 使用 CRT 和 PILERCR 识别 CRISPR 间隔区&#x…

TestHubo基础教程-创建项目

TestHubo是一款国产开源一站式测试工具&#xff0c;涵盖功能测试、接口测试、性能测试&#xff0c;以及 Web 和 App 测试&#xff0c;可以满足不同类型项目的测试需求。本文将介绍如何快速创建第一个项目&#xff0c;以快速入门上手。 1、创建项目 在 TestHubo 中&#xff0c;…

多模态基础模型第二篇-deepseek-r1部署

分别使用本地windows和云端linux进行部署&#xff0c;测试不同硬件资源的模型推理性能&#xff1a; windos部署&#xff1a;直接打开Download Ollama on Linux 下载&#xff0c;然后本地启动服务&#xff0c; linux部署&#xff1a;curl -fsSL https://ollama.ai/install.sh …

本地 Ollama 部署 Deepseek R1 并使用 Spring AI Alibaba 构建 Chat 应用示例

本地部署 Deepseek R1 并使用 Spring AI Alibaba 构建 Chat 应用示例 Ollama 部署 Deepseek R1 官网&#xff1a;https://www.deepseek.com/ Github&#xff1a;https://github.com/deepseek-ai Ollama&#xff1a;https://ollama.com/ Docker Compose 部署一个 Ollama 和…

【TI C2000】F28002x的系统延时、GPIO配置及SCI(UART)串口发送、接收

【TI C2000】F28002x的系统延时、GPIO配置及SCI&#xff08;UART&#xff09;串口发送、接收 文章目录 系统延时GPIO配置GPIO输出SCI配置SCI发送、接收测试附录&#xff1a;F28002x开发板上手、环境配置、烧录及TMS320F280025C模板工程建立F28002x叙述烧录SDK库文件说明工程建…

LabVIEW中的icon.llb 库

icon.llb 库位于 C:\Program Files (x86)\National Instruments\LabVIEW 2019\vi.lib\Platform 目录下&#xff0c;是 LabVIEW 系统中的一个重要库。它的主要功能是与图标相关的操作&#xff0c;提供了一些实用的 VI 用于处理 LabVIEW 图标的显示、修改和设置。通过该库&#x…

C语言-章节 1:变量与数据类型 ——「未初始化的诅咒」

在那神秘且广袤无垠的「比特大陆」上&#xff0c;阳光奋力地穿过「内存森林」中错综复杂的代码枝叶缝隙&#xff0c;洒下一片片斑驳陆离、如梦似幻的光影。林间的空气里&#xff0c;弥漫着一股浓郁的十六进制锈蚀味&#xff0c;仿佛在诉说着这片森林中隐藏的古老秘密。 一位零基…

Dest1ny漏洞库:用友 U8-CRM 系统 ajaxgetborrowdata.php 存在 SQL 注入漏洞

大家好&#xff0c;今天是Dest1ny漏洞库的专题&#xff01;&#xff01; 会时不时发送新的漏洞资讯&#xff01;&#xff01; 大家多多关注&#xff0c;多多点赞&#xff01;&#xff01;&#xff01; 用友U8-CRM系统ajaxgetborrowdata.php存在SQL注入漏洞&#xff0c;文件多…

全平台搭载旭日5!科沃斯GOAT智能割草机器人全新系列正式开售

要闻 近日&#xff0c;科沃斯全新发布的GOAT A Series 和 GOAT O Series割草机器人&#xff0c;将在多国市场正式上市发售。作为业界最强的割草机器人产品之一&#xff0c;GOAT致力为割草机带来基于机器人视觉的专业定位解决方案。科沃斯GOAT全新系列产品全平台搭载地瓜机器人…

HCIA综合项目之多技术的综合应用实验

十五 HCIA综合实验 15.1 IP规划 #内网分配网段192.168.1.0 24#内网包括骨干链路和两个用户网段&#xff0c;素以需要划分三个&#xff0c;借两位就够用了192.168.1.0 26--骨干192.168.1.64 26---R1下网络192.168.1.128 26---R2下网络192.168.1.192 26--备用​192.168.1.64 26--…

PbootCMS增加可允许上传文件类型,例如webp、mov等文件格式扩展

在PbootCMS日常使用过程中&#xff0c;会涉及一些非常见的文件格式上传。 这时候就需要在PbootCMS配置文件中追加一些允许上传文件扩展名。 操作步骤 1、打开/config/config.php文件&#xff0c;大约在30行&#xff0c;修改upload配置信息&#xff1a; // 上传配置upload &…

DeepSeek应用——与PyCharm的配套使用

目录 一、配置方法 二、使用方法 三、注意事项 1、插件市场无continue插件 2、无结果返回&#xff0c;且在本地模型报错 记录自己学习应用DeepSeek的过程&#xff0c;使用的是自己电脑本地部署的私有化蒸馏模型...... &#xff08;举一反三&#xff0c;这个不单单是可以用…

本地快速部署DeepSeek-R1模型以及可视化工具

这里写目录标题 安装 Ollama下载和部署DeepSeek模型可视化工具 安装 Ollama Ollama 是一个轻量级的可扩展框架&#xff0c;用于在本地计算机上构建和运行语言模型。它提供了一个用于创建、运行和管理模型的简单 API&#xff0c;以及一个可在各种应用程序中轻松使用的预构建模型…