当前位置: 首页 > news >正文

【知识点】图与图论入门

何为图论

见名知意,图论 (Graph Theory) 就是研究 图 (Graph) 的数学理论和方法。图是一种抽象的数据结构,由 节点 (Node) 和 连接这些节点的 边 (Edge) 组成。图论在计算机科学、网络分析、物流、社会网络分析等领域有广泛的应用。

如下,这就是一个图,可以看到这个图有 5 5 5 个顶点,分别编号为 { 0 , 1 , 2 , 3 , 4 } \{0, 1, 2, 3, 4\} {0,1,2,3,4}。同时这个图有 4 4 4 条边,例如,在顶点 2 2 2 和 顶点 4 4 4 之间存在着一条边。

image

图的基本概念

在详细讲解图论和有关图论算法之前,先来了解一下在图论中的一些基本表述和规范。

  1. 图 (Graph):图是一种由一组顶点和一组边组成的数据结构,记做 G = ( V , E ) G = (V, E) G=(V,E),其中 V V V 代表顶点集合, E E E​ 代表边集合。
  2. 顶点 (Vertex):顶点是图的基本单位,也称为节点。
  3. 边 (Edge):一条边是连接两个顶点的线段或弧。可以是无向的,也可以是有向的。一条边可以记做为 ( u , v ) (u, v) (u,v)。在无向图中,若存在一条 ( u , v ) (u, v) (u,v),表示可以从 u u u 点直接走到 v v v 点,反之亦然。但若在有向图中,存在一条边 ( u , v ) (u, v) (u,v),表示可以从 u u u 节点直接走向 v v v 节点,如果不存在一条边 v , u v, u v,u,那么 v v v 节点就没有办法直接走向 u u u 节点。
  4. 无向图 (Undirected Graph):图中的边没有方向,即 ( u , v ) (u, v) (u,v) ( v , u ) (v, u) (v,u) 是同一条边。
  5. 有向图 (Directed Graph/ Digraph):图中的边有方向,即 ( u , v ) (u, v) (u,v) ( v , u ) (v, u) (v,u)​ 不是同一条边。
  6. 简单图 (Simple Graph):表示含有重边(两个顶点之间的多条边)和自环(顶点到自身的边)的图。
  7. 多重图 (Multigraph):允许有重边和自环的图。
  8. 边权 (Weight of an Edge):一般表示经过这一条边的代价(代价一般是由命题人定义的)。

如下图,就是一个有向的简单图(通常来说,在有向图中边的方向用箭头来表示):

image

如下图,就是一个无向的多重图,其中存在两条边可以从顶点 5 5 5 到顶点 2 2 2

image

与此同时,为了方便起见,对于无向图的处理,我们只需要在两个顶点之间建立两个方向相反的无向边就可以表示一个无向图,具体如下:

image

图的表示方法

在计算机中,图可以通过许多方式来构建和表示。总的可以分成图的邻接矩阵和邻接表两种方法(关于链式前向星本文不过多展开叙述,有兴趣的可以自行查阅相关文档)。

图的邻接矩阵 (Adjacency Matrix)

若一个图中有 N N N 个顶点,那么我们就可以用一个 N × N N \times N N×N 的矩阵来表示这个图。我们一般定义,若矩阵的元素 A i , j ≠ − ∞ A_{i, j} \neq -\infty Ai,j= 表示从节点 i i i j j j 有一条有向边,其中边的权值为 A i , j A_{i, j} Ai,j​。

假设存在一个有 3 3 3 个顶点的图,并且有三条有向边 E = { ( 1 , 2 ) , ( 2 , 3 ) , ( 3 , 2 ) } E = \{(1, 2), (2, 3), (3, 2)\} E={(1,2),(2,3),(3,2)},那么就可以用邻接矩阵表示为:
G = [ 1 2 3 1 0 1 0 2 0 0 1 3 0 1 0 ] G = \begin{bmatrix} & \mathtt{1} & \mathtt{2} & \mathtt{3}\\ \mathtt{1} & 0 & 1 & 0 \\ \mathtt{2} & 0 & 0 & 1 \\ \mathtt{3} & 0 & 1 & 0 \end{bmatrix} G= 123100021013010
画成可视化的图就长这个样子:

image

在 C++ 中,我们可以简单地用一个二维数组来表示:

// 定义一个矩阵。
int map[50][50];// 将所有的边初始化为负无穷大。
for (int i=1; i<=50; i++)for (int j=1; j<=50; j++)map[i][j] = -0x7f7f7f7f;// 建边,其中所有的边权为1。
map[1][2] = map[2][3] = map[3][2] = 1;

图的邻接表 (Adjacency List)

邻接表本质上就是用链表表示图。数组的每个元素表示一个顶点,元素的值是一个链表,链表中存储该顶点的所有邻接顶点。假设存在一个有 4 4 4 个顶点的图,并且有四条有向边 E = { ( 1 , 2 ) , ( 2 , 3 ) , ( 3 , 2 ) , ( 3 , 4 ) } E = \{(1, 2), (2, 3), (3, 2), (3, 4)\} E={(1,2),(2,3),(3,2),(3,4)},那么就可以用邻接表表示为:

image

画成可视化的图就长这个样子:

image

在 C++ 中,我们可以使用 STL模板库 中的 vector 来实现:

#include <vector>
vector<int> G[50];  // 建图。
G[1].push_back(2);
G[2].push_back(3);
G[3].push_back(2);
G[3].push_back(4);

一般情况下,推荐使用邻接表的方式来存图,因为使用邻接矩阵比较浪费空间。在顶点数量非常多但边非常少的图中, N 2 N^2 N2 的时空复杂度会导致 MLE 或 TLE 等问题。

图的各种性质

  1. 度数 (Degree):一个顶点的度是连接该顶点的边的数量。在有向图中,有 入度 (Indegree)出度 (Outdegree) 之分(具体例子见后文)。
  2. 路径 (Path):从一个顶点到另一个顶点的顶点序列,路径上的边没有重复。
  3. 回路 (Cycle):起点和终点相同的路径。
  4. 连通图 (Connected Graph):任意两个顶点之间都有路径相连的无向图。
  5. 强连通图 (Strongly Connected Graph):任意两个顶点之间都有路径相连的有向图。

对于下面这个无向图不连通图,顶点 1 1 1 的度数为 1 1 1;顶点 2 2 2 的度数为 2 2 2;顶点 3 3 3 的度数为 1 1 1;顶点 4 4 4 的度数为 0 0 0。同时,由于 4 4 4 号顶点没有度数,所以该顶点没有办法到达任何一个其他的顶点,所以这个图是一个不连通图:

image

如下图,就是一个有向不强连通图。其中,顶点 1 1 1 的入度为 0 0 0,出度为 2 2 2;顶点 2 2 2 的入度为 1 1 1,出度也为 1 1 1;顶点 3 3 3 的入度为 2 2 2,但出度为 0 0 0。由于顶点 1 1 1 和顶点 2 2 2 可以走到顶点 3 3 3,但顶点 3 3 3 没有办法走到顶点 1 1 1 或顶点 2 2 2,因此下面的图不是一个强连通图:

image

对于下图来说, 1 → 2 → 3 → 4 1\to 2\to 3\to 4 1234 是一条从顶点 1 1 1 到顶点 4 4 4的路径。 2 → 3 → 4 → 2 → 3 2\to 3\to 4 \to 2\to 3 23423 就不是一个路径,因为相同的边 ( 2 , 3 ) (2, 3) (2,3) 被多次走到了。 1 → 2 → 3 → 1 1\to 2\to 3\to 1 1231 就是一个回路,因为这个路径的起点和终点相同:

image

图的遍历

图通常采用 深度优先搜索/ 广度优先搜索 这两个算法来遍历。其中深度优先算法是最常见的遍历算法。

对于一个用 邻接矩阵 保存的图,其深度优先搜索遍历的 C++ 代码如下:

int vis[105], map[105][105];void dfs(int node){if (vis[node]) return ;vis[node] = 1;cout << node << endl;for (int i=1; i<=n; i++)if (map[node][i] != -0x7f7f7f7f)dfs(i);return ;
}// 函数调用:dfs(1); 表示从1号顶点开始遍历。

对于一个用 邻接表 保存的图,其深度优先搜索遍历的 C++ 代码如下:

#include <vector>
vector<int> G[105];
int vis[105];void dfs(int node){if (vis[node]) return ;vis[node] = 1;cout << node << endl;for (int to : G[node])dfs(to);return ;
}// 函数调用:dfs(1); 表示从1号顶点开始遍历。

广度优先搜索的方式也类似:

#include <queue>
vector<int> G[105];
int vis[105];void bfs(int node){queue<int> que;que.push(node);while(!que.empty()){int t = que.front();cout << t << endl;que.pop();for (int to : G[node]){if (!vis[to]) {vis[to] = 1;que.push(to);}}}return ;
}// 函数调用:bfs(1); 表示从1号顶点开始遍历。

对于判断无向图的连通性,我们只需要从任意一个点开始跑一遍深搜或者广搜就行了。如果所有顶点的 vis 都被标记了,则证明图是联通的,否则图就是不连通的。

例题讲解

P3916 图的遍历

模板题目,从每一个顶点开始用深搜遍历一遍就可以了。但从每一个点考虑能走到的最大点比较麻烦,一个更优的解决办法是反向建边,从最大的点开始遍历,这样子就可以一次性计算出多个结果。

#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;const int N = 10005;
int n, m, ans, vis[N];
vector<int> G[N];void dfs(int node, int d){if (vis[node]) return ;vis[node] = d;ans = max(node, ans);for (int to : G[node]) dfs(to, d);return ;
}int main(){cin >> n >> m;for (int i=0, u, v; i<m; i++){cin >> u >> v;G[v].push_back(u);  // 反向建边。}for (int i=n; i>=1; i--) dfs(i, i);for (int i=1; i<=n; i++) cout << vis[i] << ' ';return 0;
}

P5318 【深基18.例3】查找文献

也是一道模板题目,正常遍历即可。

#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
const int MAXN = 100005;int n, m;
int vis1[MAXN], vis2[MAXN];
queue<int> que;
vector<int> G[MAXN];void dfs(int node, int current){vis1[node] = 1;cout << node << ' ';if (current == n) return ;for (int i=0; i<G[node].size(); i++){if (vis1[G[node][i]]) continue;dfs(G[node][i], current+1);}return ;
}void dfs(int node){vis2[node] = 1;que.push(node);while(que.size()){int t = que.front();cout << t << " ";for (int i=0; i<G[t].size(); i++){if (vis2[G[t][i]]) continue;vis2[G[t][i]] = 1;que.push(G[t][i]);}que.pop();}return ;
}int main(){cin >> n >> m;for (int i=0; i<m; i++){int t1, t2;cin >> t1 >> t2;G[t1].push_back(t2);}for (int i=1; i<=n; i++) sort(G[i].begin(), G[i].end());dfs(1, 0), cout << endl, dfs(1);return 0;
}

番外 - 图的常见算法

更多关于图论的算法,请持续关注后续更新。

  1. 深度优先搜索 (DFS):适用于遍历图和检测图中的回路。
  2. 广度优先搜索 (BFS):适用于寻找最短路径(无权图)。
  3. Dijkstra 算法:适用于加权图中寻找单源最短路径。
  4. Bellman-Ford 算法:适用于有负权边的图中寻找单源最短路径。
  5. Floyd-Warshall 算法:适用于寻找所有顶点对之间的最短路径。
  6. Kruskal 算法:用于求解最小生成树 (MST - Minimum Spanning Tree)。
  7. Prim 算法:另一种求解最小生成树的方法。
  8. 拓扑排序 (Topological Sorting):适用于有向无环图 (DAG),用于任务调度等应用。
  9. Tarjan 算法:用于求解图中的强连通分量、割点、桥。

相关文章:

【知识点】图与图论入门

何为图论 见名知意&#xff0c;图论 (Graph Theory) 就是研究 图 (Graph) 的数学理论和方法。图是一种抽象的数据结构&#xff0c;由 节点 (Node) 和 连接这些节点的 边 (Edge) 组成。图论在计算机科学、网络分析、物流、社会网络分析等领域有广泛的应用。 如下&#xff0c;这…...

FPGA系列,文章目录

前言 FPGA&#xff08;Field-Programmable Gate Array&#xff0c;现场可编程门阵列&#xff09;是一种集成电路&#xff0c;其内部结构可以通过软件重新配置来实现不同的逻辑功能。与传统的ASIC&#xff08;Application-Specific Integrated Circuit&#xff0c;专用集成电路…...

PAT乙级1003我要通过的做题笔记

分析题意 得到“答案正确”的条件是&#xff1a; 字符串中必须仅有 P、 A、 T这三种字符&#xff0c;不可以包含其它字符&#xff1b; 任意形如 xPATx 的字符串都可以获得“答案正确”&#xff0c;其中 x 或者是空字符串&#xff0c;或者是仅由字母 A 组成的字符串&#xff1…...

【React】React常用开发工具

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、React DevTools二、Redux DevTools三、Create React App 前言 React 是一种用于构建用户界面的流行 JavaScript 库&#xff0c;由于其灵活性、性能和可重用…...

Ubuntu20.04编译安装Carla全过程

前言 Carla的安装是我现阶段解决的第一个问题&#xff0c;现记录一下我安装Carla的过程以及我在安装过程中遇到的一些问题。 一、安装前准备 1、硬件环境 carla是一款基于UE4开发的模拟仿真软件&#xff0c;本身对硬件的要求比较高。 我是windows与ubuntu双系统&#xff0…...

Dijkstra 算法 是什么?

Dijkstra 算法 Dijkstra 算法是一种经典的最短路径算法&#xff0c;用于在图&#xff08;有向或无向图&#xff09;中找到从起点到其他所有节点的最短路径。它以广度优先搜索的方式&#xff0c;逐步扩展到目标节点&#xff0c;确保计算出的路径是最短的。 1. Dijkstra 算法的基…...

英文输入法---华为OD机试2024年E卷

题解&#xff1a; 代码&#xff1a;...

理解 package.json 中版本号符号

今天&#xff0c;聊一聊在前端开发中&#xff0c; package.json 中怎么看版本号符号。 版本号符号的解释 版本号通常由三部分组成&#xff1a;主版本号、次版本号、补丁版本号&#xff0c;格式为 major.minor.patch。常见的符号有&#xff1a; ^&#xff1a;更新时允许自动…...

计算机网络-IPSec VPN基本概念

企业分支之间经常有互联的需求&#xff0c;企业互联的方式很多&#xff0c;可以使用专线线路或者Internet线路。部分企业从成本和需求出发会选择使用Internet线路进行互联&#xff0c;但是使用Internet线路存在安全风险&#xff0c;如何保障数据在传输时不会被窃取&#xff1f;…...

VsCode运行Ts文件

1. 生成package.json文件 npm init 2. 生成tsconfig.json文件 tsc --init 3. Vscode运行ts文件 在ts文件点击右键执行Run Code,执行ts文件...

模型 AITDA(吸引、兴趣、信任、渴望、行动)

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。吸引、兴趣、信任、渴望、行动 五步曲。 1 模型AITDA的应用 1.1 开源AI智能名片小程序的营销策略 一家企业开发了开源AI智能名片小程序&#xff0c;旨在通过S2B2C模式连接供应商和消费者。该企业采用…...

十、软件设计架构-微服务-服务调用Feign

文章目录 前言一、Feign介绍1. 什么是Feign2. 什么是Http客户端3. Feign 和 OpenFeign 的区别 二、Feign底层原理三、Feign工作原理详解1. 动态代理机制2. 动态代理的创建过程3. 创建详细流程4. FeignClient属性 四、Feign使用1. 常规调用2.日志打印3. 添加Header 前言 服务调…...

电子商务人工智能指南 3/6 - 聊天机器人和客户服务

介绍 81% 的零售业高管表示&#xff0c; AI 至少在其组织中发挥了中等至完全的作用。然而&#xff0c;78% 的受访零售业高管表示&#xff0c;很难跟上不断发展的 AI 格局。 近年来&#xff0c;电子商务团队加快了适应新客户偏好和创造卓越数字购物体验的需求。采用 AI 不再是一…...

【AI模型对比】Kimi与ChatGPT的差距:真实对比它们在六大题型中的全面表现!

文章目录 Moss前沿AI语义理解文学知识数学计算天文学知识物理学知识英语阅读理解详细对比列表总结与建议 Moss前沿AI 【OpenAI】获取OpenAI API Key的多种方式全攻略&#xff1a;从入门到精通&#xff0c;再到详解教程&#xff01;&#xff01; 【VScode】VSCode中的智能AI-G…...

spring6:2入门

spring6&#xff1a;2入门 目录 spring6&#xff1a;2入门2.1、环境要求2.2、构建模块2.3、程序开发2.3.1、引入依赖2.3.2、创建java类2.3.3、创建配置文件2.3.4、创建测试类测试2.3.5、运行测试程序 2.4、程序分析2.5、启用Log4j2日志框架2.5.1、Log4j2日志概述2.5.2、引入Log…...

Netty - NIO基础学习

一 简介 1 三大模型是什么&#xff1f; IO三大模型之一&#xff0c;BIO&#xff0c;AIO&#xff0c;还有我们的主角NIO(non-blocking-io)&#xff0c;也就是同步非阻塞式IO。这三种模型到底是干什么的&#xff1f;其实这三种模型都是对于JAVA的一种I/O框架&#xff0c;用来进行…...

ArrayList的自动扩容机制源码

Java的ArrayList的自动扩容机制 ArrayList是 Java 中极为常用的动态数组实现类&#xff0c;它依托数组存储数据&#xff0c;能依据实际需求灵活变动容量&#xff0c;高效管理元素集合。在深挖底层源码细节前&#xff0c;先来了解创建ArrayList集合并添加元素时的运作流程&#…...

【llm_inference】react框架(最小code实现)

ReAct&#xff1a;结合推理和行动的大语言模型推理架构 GitHub Code: 人人都能看懂的最小实现 引言 在人工智能领域&#xff0c;大语言模型&#xff08;LLM&#xff09;的应用日益广泛&#xff0c;但如何让模型能够像人类一样&#xff0c;在思考的基础上采取行动&#xff0c…...

PT8M2103 触控 I/O 型 8-Bit MCU

1 产品概述 ● PT8M2103 是一款可多次编程(MTP)I/O 型8位 MCU&#xff0c;其包括 2K*16bit MTP ROM、256*8bit SRAM、PWM、Touch 等功能&#xff0c;具有高性能精简指令集、低工作电压、低功耗特性且完全集成触控按键功能。为各种触控按键的应用&#xff0c;提供了一种简单而又…...

英语时态学习+名词副词形容词变形方式

开发出头不容易 不如跨界卷英语 英语中的16种时态是由四种时间&#xff08;现在、过去、将来、过去将来&#xff09;和四种体&#xff08;一般、进行、完成、完成进行&#xff09;组合而成的。以下是每种时态的详细说明和例句&#xff1a; 一般现在时 (Simple Present) 用法…...

python/java环境配置

环境变量放一起 python&#xff1a; 1.首先下载Python Python下载地址&#xff1a;Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个&#xff0c;然后自定义&#xff0c;全选 可以把前4个选上 3.环境配置 1&#xff09;搜高级系统设置 2…...

渲染学进阶内容——模型

最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

Linux云原生安全:零信任架构与机密计算

Linux云原生安全&#xff1a;零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言&#xff1a;云原生安全的范式革命 随着云原生技术的普及&#xff0c;安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测&#xff0c;到2025年&#xff0c;零信任架构将成为超…...

06 Deep learning神经网络编程基础 激活函数 --吴恩达

深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)

前言&#xff1a; 最近在做行为检测相关的模型&#xff0c;用的是时空图卷积网络&#xff08;STGCN&#xff09;&#xff0c;但原有kinetic-400数据集数据质量较低&#xff0c;需要进行细粒度的标注&#xff0c;同时粗略搜了下已有开源工具基本都集中于图像分割这块&#xff0c…...

Python竞赛环境搭建全攻略

Python环境搭建竞赛技术文章大纲 竞赛背景与意义 竞赛的目的与价值Python在竞赛中的应用场景环境搭建对竞赛效率的影响 竞赛环境需求分析 常见竞赛类型&#xff08;算法、数据分析、机器学习等&#xff09;不同竞赛对Python版本及库的要求硬件与操作系统的兼容性问题 Pyth…...

一些实用的chrome扩展0x01

简介 浏览器扩展程序有助于自动化任务、查找隐藏的漏洞、隐藏自身痕迹。以下列出了一些必备扩展程序&#xff0c;无论是测试应用程序、搜寻漏洞还是收集情报&#xff0c;它们都能提升工作流程。 FoxyProxy 代理管理工具&#xff0c;此扩展简化了使用代理&#xff08;如 Burp…...

Appium下载安装配置保姆教程(图文详解)

目录 一、Appium软件介绍 1.特点 2.工作原理 3.应用场景 二、环境准备 安装 Node.js 安装 Appium 安装 JDK 安装 Android SDK 安装Python及依赖包 三、安装教程 1.Node.js安装 1.1.下载Node 1.2.安装程序 1.3.配置npm仓储和缓存 1.4. 配置环境 1.5.测试Node.j…...

Redis上篇--知识点总结

Redis上篇–解析 本文大部分知识整理自网上&#xff0c;在正文结束后都会附上参考地址。如果想要深入或者详细学习可以通过文末链接跳转学习。 1. 基本介绍 Redis 是一个开源的、高性能的 内存键值数据库&#xff0c;Redis 的键值对中的 key 就是字符串对象&#xff0c;而 val…...

Electron简介(附电子书学习资料)

一、什么是Electron&#xff1f; Electron 是一个由 GitHub 开发的 开源框架&#xff0c;允许开发者使用 Web技术&#xff08;HTML、CSS、JavaScript&#xff09; 构建跨平台的桌面应用程序&#xff08;Windows、macOS、Linux&#xff09;。它将 Chromium浏览器内核 和 Node.j…...