图论中的核心C++算法,包括存储结构、核心思路、速记口诀以及学习方法, 一站式上机考试学习

news/2025/11/28 0:30:14/文章来源:https://www.cnblogs.com/kkman2000/p/19279841

一、图的存储结构

不同算法适配不同的存储结构,选择合适的存储方式是实现算法的基础:

1. 邻接矩阵(二维数组)

// 适用于稠密图,顶点数n较小的情况
const int MAXN = 1005;
int graph[MAXN][MAXN]; // graph[i][j]表示i到j的边权,无边时设为INF
  • 特点:访问边的时间复杂度O(1),空间复杂度O(n²),适合顶点数少的场景(如Floyd算法)。

2. 邻接表(vector/链表)

// 适用于稀疏图,顶点数n大、边数m少的情况
const int MAXN = 1e5 + 5;
vector<pair<int, int>> adj[MAXN]; // adj[u]存储(u, v, w),即u到v的边权为w
  • 特点:空间复杂度O(m),遍历邻接点高效,适合DFS/BFS/Dijkstra/Prim等算法。

3. 边集数组(结构体数组)

// 适用于按边操作的算法(如Kruskal)
struct Edge {int u, v, w;bool operator<(const Edge& other) const {return w < other.w; // 按边权排序}
} edges[MAXM]; // MAXM为边数
  • 特点:直接存储所有边,便于排序和筛选,是Kruskal算法的首选。

二、各算法详解(存储结构+核心思路+速记口诀)

1. 图的遍历:DFS(深度优先搜索)

存储结构

邻接表(优先)或邻接矩阵。

核心思路

  • 从起点出发,沿着一条路径走到头(递归/栈实现);
  • 回溯后探索其他未访问的分支,标记已访问节点避免重复。

代码框架(邻接表)

bool visited[MAXN];
void dfs(int u) {visited[u] = true; // 标记访问for (auto& [v, w] : adj[u]) { // 遍历邻接点if (!visited[v]) {dfs(v); // 递归访问}}
}

速记口诀

“一条路走到黑,回头再探其他路”

2. 图的遍历:BFS(广度优先搜索)

存储结构

邻接表(优先)或邻接矩阵。

核心思路

  • 从起点出发,先访问所有邻接点(第一层);
  • 再依次访问邻接点的邻接点(第二层),用队列实现“逐层扩散”。

代码框架(邻接表)

bool visited[MAXN];
void bfs(int start) {queue<int> q;q.push(start);visited[start] = true;while (!q.empty()) {int u = q.front(); q.pop();for (auto& [v, w] : adj[u]) {if (!visited[v]) {visited[v] = true;q.push(v);}}}
}

速记口诀

“队列排队,逐层扫荡,先近后远”

3. 最短路:Dijkstra(单源最短路径,无负权边)

存储结构

邻接表(优先)+ 优先队列(小根堆)。

核心思路

  • 初始化起点到各点的距离为INF,起点距离为0;
  • 用优先队列选当前距离最小的节点u,松弛其邻接点v(即dist[v] = min(dist[v], dist[u]+w));
  • 重复直到所有节点处理完毕。

代码框架(邻接表+优先队列)

const int INF = 0x3f3f3f3f;
int dist[MAXN];
void dijkstra(int start, int n) {memset(dist, 0x3f, sizeof(dist));dist[start] = 0;priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq;pq.push({0, start});while (!pq.empty()) {auto [d, u] = pq.top(); pq.pop();if (d > dist[u]) continue; // 跳过已处理的旧节点for (auto& [v, w] : adj[u]) {if (dist[v] > dist[u] + w) {dist[v] = dist[u] + w;pq.push({dist[v], v});}}}
}

速记口诀

“小堆选近点,松弛邻接点,贪心找最短”

4. 最短路:Floyd(多源最短路径,允许负权边无负环)

存储结构

邻接矩阵(必须)。

核心思路

  • 动态规划思想:dp[k][i][j]表示经过前k个节点时i到j的最短路径;
  • 状态转移:dp[i][j] = min(dp[i][j], dp[i][k]+dp[k][j])(k为中转点);
  • 三层循环:枚举中转点k→起点i→终点j。

代码框架(邻接矩阵)

const int INF = 0x3f3f3f3f;
int dp[MAXN][MAXN];
void floyd(int n) {// 初始化dp为邻接矩阵for (int k = 1; k <= n; k++) { // 中转点for (int i = 1; i <= n; i++) { // 起点for (int j = 1; j <= n; j++) { // 终点dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j]);}}}
}

速记口诀

“中转点插中间,三层循环算遍,dp找最短”

5. 最小生成树:Prim(稠密图适用)

存储结构

邻接矩阵(稠密图)或邻接表+优先队列(稀疏图)。

核心思路

  • 从任意起点出发,维护“已选点集合”;
  • 每次选连接“已选集合”和“未选集合”的最小权边,将对应点加入集合;
  • 重复直到所有点加入(共选n-1条边)。

代码框架(邻接表+优先队列)

const int INF = 0x3f3f3f3f;
int dist[MAXN]; // dist[v]表示v到已选集合的最小距离
bool visited[MAXN];
int prim(int n) {memset(dist, 0x3f, sizeof(dist));memset(visited, false, sizeof(visited));dist[1] = 0; // 从1号点开始int sum = 0; // 最小生成树总权值for (int i = 1; i <= n; i++) {// 选未访问的最小dist节点int u = -1;for (int j = 1; j <= n; j++) {if (!visited[j] && (u == -1 || dist[j] < dist[u])) {u = j;}}visited[u] = true;sum += dist[u];// 更新邻接点到已选集合的距离for (auto& [v, w] : adj[u]) {if (!visited[v] && w < dist[v]) {dist[v] = w;}}}return sum;
}

速记口诀

“选点扩集合,贪最小边连,n-1边成团”

6. 最小生成树:Kruskal(稀疏图适用)

存储结构

边集数组(必须)+ 并查集(判环)。

核心思路

  • 将所有边按权值从小到大排序;
  • 依次选边,若边的两个端点不在同一连通分量(用并查集判断),则加入生成树;
  • 重复直到选n-1条边。

代码框架(边集数组+并查集)

struct Edge { int u, v, w; };
Edge edges[MAXM];
int parent[MAXN];// 并查集查找(带路径压缩)
int find(int x) {return parent[x] == x ? x : parent[x] = find(parent[x]);
}int kruskal(int n, int m) {sort(edges, edges + m); // 按边权排序for (int i = 1; i <= n; i++) parent[i] = i; // 初始化并查集int sum = 0, cnt = 0; // sum总权值,cnt选边数for (int i = 0; i < m; i++) {int u = edges[i].u, v = edges[i].v, w = edges[i].w;int fu = find(u), fv = find(v);if (fu != fv) { // 不连通则合并parent[fu] = fv;sum += w;cnt++;if (cnt == n-1) break; // 选够n-1条边}}return sum;
}

速记口诀

“边排序选小,并查集判环,连n-1边好”

7. 拓扑排序:Kahn算法(处理DAG)

存储结构

邻接表(存边)+ 入度数组(存节点入度)。

核心思路

  • 初始化队列,将入度为0的节点入队;
  • 取出队首节点u,加入拓扑序列,遍历其邻接点v,将v的入度减1;
  • 若v的入度变为0则入队,重复直到队空;
  • 若拓扑序列长度≠节点数,说明有环。

代码框架(邻接表+入度数组)

vector<int> adj[MAXN];
int in_degree[MAXN];
vector<int> topo_sort(int n) {queue<int> q;vector<int> res;for (int i = 1; i <= n; i++) {if (in_degree[i] == 0) q.push(i);}while (!q.empty()) {int u = q.front(); q.pop();res.push_back(u);for (int v : adj[u]) {in_degree[v]--;if (in_degree[v] == 0) q.push(v);}}return res; // 若res.size() < n则有环
}

速记口诀

“入度零入队,删边减度数,依次排顺序”

8. 关键路径算法(处理DAG的最长路径)

存储结构

邻接表(存边)+ 逆邻接表(用于求ve/vl)。

核心思路

  • 步骤1:拓扑排序,求事件最早发生时间ve[i]ve[v] = max(ve[v], ve[u]+w));
  • 步骤2:逆拓扑排序,求事件最迟发生时间vl[i]vl[u] = min(vl[u], vl[v]-w));
  • 步骤3:计算活动最早/最迟开始时间,若相等则为关键活动,关键活动组成关键路径。

速记口诀

“拓扑算ve,逆序算vl,相等是关键”

三、算法速查表

算法 存图结构 核心 3 步 口诀(10 字内) 模板行数
DFS/BFS 邻接表 vector<int> g[N] 1.队列/栈 2.访标记 3.拓邻居 “栈深队广,标邻” 10 行
Dijkstra 邻接表 vector<P> g[N] 1.小根堆 2.松弛 3.vis 防重 “堆松 vis” 15 行
Floyd 矩阵 int d[N][N] 1.自环初值 2.kij 中转 3.更优更新 “kij 中转” 5 行
Prim 邻接表 vector<P> g[N] 1.小根堆 2.跨边入堆 3.累加答案 “跨边堆” 15 行
Kruskal 边集 vector<Edge> e 1.按权排序 2.并查加边 3.n-1 结束 “排并加” 12 行
Khan 拓扑 邻接表 + 入度数组 1.入度 0 入队 2.删边降度 3.队空判环 “0 队降” 12 行
关键路径 邻接表 + 逆邻接表 1.拓扑正序算 ve 2.逆序算 vl 3.边 e=l=ve, l=vl-w, 关键=e==l “正 ve 逆 vl” 20 行

四、学习总结与推荐

学习步骤推荐

  1. 先把口诀抄三遍,读出声,强化核心逻辑记忆;
  2. 把模板打印,在空白处默写核心3步,理解算法骨架;
  3. 上机刷5~10道经典裸题,巩固代码实现能力。

经典练习题单

算法 百炼题号(标题) 直达链接
DFS/BFS 2810 - 迷宫问题 https://bailian.openjudge.cn/practice/2810
BFS 最短路 2796 - 骑士移动 https://bailian.openjudge.cn/practice/2796
Dijkstra 2724 - 最短路 https://bailian.openjudge.cn/practice/2724
Floyd 2725 - 全源最短路 https://bailian.openjudge.cn/practice/2725
Prim 2727 - 最小生成树 https://bailian.openjudge.cn/practice/2727
Kruskal 2728 - 丛林之路 https://bailian.openjudge.cn/practice/2728
Khan 拓扑 2722 - 拓扑排序 https://bailian.openjudge.cn/practice/2722
关键路径 2730 - 关键路径 https://bailian.openjudge.cn/practice/2730
综合 DFS+剪枝 2692 - 八皇后问题 https://bailian.openjudge.cn/practice/2692
综合 最短路+堆优化 2757 - 网络延时 https://bailian.openjudge.cn/practice/2757

总结

  • 存储结构选型:邻接表适配多数遍历/最短路径/生成树算法,邻接矩阵是Floyd专属,边集数组为Kruskal定制;
  • 算法核心逻辑:DFS/BFS是遍历基础,Dijkstra/Floyd解决最短路径,Prim/Kruskal构建最小生成树,拓扑排序处理DAG依赖,关键路径分析DAG最长路径;
  • 学习关键方法:口诀记核心、模板搭骨架、刷题练实战,三步结合可高效掌握图论算法。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

hive 中 group by 和 distinct 孰优孰劣?

首先声明一下,hive是什么: hive 不是数据库,hive 只是一个数据仓库工具,可以用来查询、转化和加载数据,是可以调用 mapreduce 任务、用类 mysql 语法查询HDFS数据的一个工具。再来说 mapreduce 是什么,mapreduce…

DDD抽奖项目业务回顾

抽奖系统架构 模块概览模块名称 DDD 分层 职责描述trigger 接口层 (Interface Layer) 负责与外部系统交互,接收请求、身份验证、日志记录,并将请求转发给应用层。api 接口契约 (Contracts) 独立的服务契约定义,包含…

黑马程序员SpringCloud微服务开发与实战-微服务-服务拆分02

黑马程序员SpringCloud微服务开发与实战-微服务-服务拆分02Posted on 2025-11-28 00:17 心默默言 阅读(0) 评论(0) 收藏 举报接下来,我们就一起将黑马商城这个单体项目拆分为微服务项目,并解决其中出现的各种问…

API设计最佳实践 - 智慧园区

后端工程师的API设计与开发实战指南:从原则到部署作为一名后端开发,日常工作中一大部分时间都在和API打交道。它就像是整个应用的「服务员」,前端、移动端或者其他服务想要什么数据、执行什么操作,都得通过它。一个…

Python高阶知识点整理

数据类型 常用方法 (Method) 简要说明与应用场景字符串 str .strip() 去除首尾空格(常用于处理用户输入).split(sep) 按指定分隔符分割成列表(处理CSV数据常用).join(iter) 将列表元素合并成字符串(.split的逆操作…

第4单元检测卷

第4单元检测卷 - 题目、答案与解析 一、单项选择题题目:在Python中,用于创建一个新字典的语法是 ( )A. d = dict[] B. d = {} C. d = () D. d = new dict()答案:B 解析:在Python中,创建字典最常用和直接的方法是使…

javascript下载文件五种方式

javascript下载文件五种方式参考:https://blog.csdn.net/weixin_42705100/article/details/133125521 本文介绍了五种在JavaScript中下载文件的方法:通过`window.location.href`、`window.open()`、iframe、动态a标签…

ubunutu连接蓝牙键盘鼠标

​ 双系统ubunutu能连接上蓝牙耳机,但是win能连接上蓝牙键盘鼠标ubunutu却连接不上,百思不得其解。怀疑要么是驱动要没是配置的问题。试试了一下现有文章的方法,都是要让装bluez和blueman之类,装完之后还是搜索不到…

详细介绍:从 1.0 到 13.0:C# 十八年进化史,一部写给开发者的语言成长记

详细介绍:从 1.0 到 13.0:C# 十八年进化史,一部写给开发者的语言成长记2025-11-28 00:02 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: …

生研界:技术赋能,AI如何重塑医学科研生态?

在科技浪潮席卷全球的当下,人工智能(AI)正以前所未有的速度重塑医学科研生态。从靶点发现到药物设计,从疾病预测到精准诊疗,AI技术正逐步渗透至医学科研的每一个环节,推动着医学研究范式的深刻变革。在这场变革中…

2025ICPC区域赛成都站记——为者败之,执者失之

打银2025年的成都站是我有生以来第一次线下xcpc比赛。在先前的网络赛选拔中,我们队获得了两个icpc名额和1个ccpc名额。先前西安站我们学校打了两块金牌的情况下,我们队的三个人都非常希望在大学的第一场比赛就能打出…

quickfox windows 海外回国加速器 会导致部分国外网站不能使用

比如说 google.com可以,但是grok.com不能打开 关掉quickfox后,grok.com就能打开了

4433

用StegSolve分析图片看到一个二维码,在https://cli.im/deqr/other 扫描二维码猜测为摩斯密码...--.----...--..但没有分隔,根据题目提示4433,分隔为...-/-.--/--./..-/-..,在https://www.iamwawa.cn/morse.html 解…

在VMware Workstation设置虚拟机的VNC连接功能

在VMware Workstation设置虚拟机的VNC连接功能在VMware Workstation设置虚拟机的VNC连接功能。首先在workstation中设置好虚拟机的VNC连接参数,如图:使用VNC客户端开始连接虚拟机,配置如下: 这时候出现错误提示:…

rust基础第三篇:所有权

rust基础第三篇:所有权值被唯一的scope拥有,它们共存亡。 值可以从一个scope移动到另一个scope,新的scope会拥有这个值。 一个值可以有多个只读引用和单个可变引用,它们之间是互斥关系。 引用不能超越值的存活期。…

Houdini软件简介

Houdini软件简介Houdini(wiki,chs)是一款由加拿大Side Effects Software Inc.(简称SESI)公司开发的3D动画软件,可运行于Linux、Windows、Mac OS等操作系统 与其它的三维软件相比,其结构、操作方式等有很大的差异…

Windows系统磁盘管理——迁移“恢复分区”

方案一、将“恢复分区”迁移到“新分区”将“恢复分区(例如分区 4)”迁移到新分区(分区F)中。1.1 创建新分区 在磁盘 0 的最右侧分出一块新分区(分区 F),其容量需略大于当前的“恢复分区”。 1.2 给“恢复分区”…

WinFormedge 增加登录页面(自定义布局,非左右布局) 并作为启动页面 及两种布局切换全屏非全屏

WinFormedge 增加登录页面(自定义布局,非左右布局)并作为启动页面的实现方法 1、增加一个自定义布局(Components/Layout下新建EmptyLayout.razor布局文件) 代码如下:@inherits LayoutComponentBase@Body <Fl…

2025.11.27总结

完成儿童故事的项目 完成CS架构的项目 目前bs架构的方向还没定,不过我打算在设计上就搞好这个项目,我认为只要能设计好,从目的,数据库设计,方法接口设计。清晰的将模块拆分,功能罗列出来。 这样在编码阶段才能有…

第6单元检测卷

好的,这是根据您提供的第三份PDF文档(第6单元检测卷)内容提取的题目、答案和解析,已按题型分类,并以Markdown格式呈现。第6单元检测卷 - 题目、答案与解析 一、单项选择题题目:关于数据库存储描述正确的是 ( )A.…