GESP C++ 8级 复习资料和历年真题解析.这个系统写完了,我厉害不?
- 2026-11-08 23:57:27
适用考试:CCF 编程能力等级认证(GESP)C++ 八级
考试时间:180分钟 · 满分:100分 题型:单选题15题(30分,每题2分)+ 判断题10题(20分,每题2分)+ 编程题2题(50分,每题25分) 资料基于 官方八级大纲(C++部分,8大知识块) 和 2023年12月~2026年6月共10套真题 整理
SYLLABUS 01
考试大纲总览
一、七级与八级的区别——「从图论基础到算法综合应用,从简单DP到复杂分析与优化」
七级的核心是图的定义与遍历、复杂DP(二维DP/区间DP/LCS)、哈希表、图论算法,而八级发生了质的飞跃:
七级:学的是图怎么定义、怎么遍历,复杂DP怎么写,哈希表怎么用。
八级:学的是组合数学(计数原理、排列组合、杨辉三角)、倍增法、代数与平面几何、图论算法综合应用(最小生成树、最短路径的完整实现)、算法复杂度分析与算法优化
八级新增的核心内容:
计数原理
与七级的关系:全新内容
说明:加法原理与乘法原理,是组合数学的基础
排列与组合
与七级的关系:全新内容
说明:排列数、组合数的计算与编程实现
杨辉三角
与七级的关系:全新内容
说明:帕斯卡三角形,与组合数的关系,二项式定理
倍增法
与七级的关系:全新内容
说明:快速幂、RMQ(区间最值查询)、LCA(最近公共祖先)
代数与平面几何
与七级的关系:全新内容
说明:一元/二元方程、三角形/圆形/长方形面积、勾股定理
图论算法综合应用
与七级的关系:七级图论基础 → 深化
说明:最小生成树(Kruskal/Prim)、单源最短路(Dijkstra)、Floyd算法
算法复杂度分析
与七级的关系:七级有涉及 → 深化
说明:能分析各类算法的时间/空间复杂度,包括排序、查找、图遍历、DP、分治等
算法优化
与七级的关系:全新内容
说明:数学知识优化、时间/空间权衡、不同算法差异分析
二、考试内容——8大知识块
根据官方八级大纲(知识点详述8条)和10套真题的统计:
①
知识块:计数原理
核心知识点:加法原理、乘法原理、两者区别与综合使用
难度:★★★
考试占比:约8%
②
知识块:排列与组合
核心知识点:排列数、组合数、有重复元素排列、有限制条件排列、相邻/不相邻问题
难度:★★★★
考试占比:约15%
③
知识块:杨辉三角与二项式定理
核心知识点:杨辉三角定义与性质、二项式系数、二项式展开系数之和为2ⁿ
难度:★★★
考试占比:约8%
④
知识块:倍增法
核心知识点:快速幂、RMQ(区间最值查询)、LCA(最近公共祖先)、时间复杂度分析
难度:★★★★
考试占比:约5%
⑤
知识块:代数与平面几何
核心知识点:一元一次方程、二元一次方程、三角形/圆形/长方形面积、勾股定理、三点共线
难度:★★
考试占比:约5%
⑥
知识块:图论算法及综合应用
核心知识点:最小生成树(Kruskal/Prim)、单源最短路(Dijkstra)、Floyd算法、算法比较
难度:★★★★★
考试占比:约25%
⑦
知识块:算法的时间和空间效率分析
核心知识点:各类算法复杂度分析、排序/查找/图遍历/搜索/分治/DP复杂度
难度:★★★★
考试占比:约15%
⑧
知识块:算法优化
核心知识点:数学知识优化、时间与空间权衡、不同算法差异分析
难度:★★★★
考试占比:约7%
八级最核心的两大主线:
1. 图论算法综合应用(最小生成树 + 最短路径)—— 覆盖约25%的题目,外加编程题绝对主力
2. 排列组合与计数原理(计数原理 + 排列组合 + 杨辉三角)—— 覆盖约31%的题目,是选择题和判断题的绝对主力
KNOWLEDGE 02
各知识点详解
知识块①:计数原理——「加法原理与乘法原理」
1.1 加法原理——「要么……要么……,各种情况相加」
定义: 如果完成一件事有n类不同的方案,第1类方案有m₁种方法,第2类方案有m₂种方法,……,第n类方案有mₙ种方法,且这些方案互不重叠,那么完成这件事共有 m₁ + m₂ + … + mₙ 种方法。
(2025年12月判断第1题) 练习题: 若一项任务可用两种互斥方案完成:方案A有m种做法,方案B有n种做法,则总做法数为m+n。
答案:√。这就是加法原理的直接应用。
1.2 乘法原理——「先……再……,各步相乘」
定义: 如果完成一件事需要分成n个步骤,第1步有m₁种方法,第2步有m₂种方法,……,第n步有mₙ种方法,且各步骤之间相互独立,那么完成这件事共有 m₁ × m₂ × … × mₙ 种方法。
1.3 加法原理与乘法原理的区别
适用场景
加法原理:分类完成(「要么选A,要么选B」)
乘法原理:分步完成(「先做A,再做B」)
关键词
加法原理:「或者」、「任选其一」
乘法原理:「并且」、「先……再……」
运算
加法原理:加法(各类相加)
乘法原理:乘法(各步相乘)
互斥性
加法原理:各类方案互不重叠
乘法原理:各步骤相互独立
(2025年12月第2题) 练习题: 用0~9组成没有重复数字的三位数,百位不能为0,有多少种?
解析: 分三步:百位1~9(9种),十位0~9中除去百位已选的(9种),个位0~9中除去百位和十位已选的(8种)。总方案数 = 9 × 9 × 8 = 648。
知识块②:排列与组合——「选出来是组合,排好序是排列」
2.1 排列——「顺序重要」
排列数: 从n个不同元素中取出m个(m≤n)元素,按照一定顺序排成一列,称为排列。所有排列的个数称为排列数。
公式: P(n, m) = n × (n-1) × (n-2) × … × (n-m+1) = n! / (n-m)!
(2024年6月第1题) 练习题: ABCDE五个小朋友,排成一队跑步,其中AB两人必须排在一起,一共有多少种排法?
解析: 将AB看作一个整体,有4个「元素」排列:4! = 24种。AB内部可以互换:2! = 2种。总方案数 = 24 × 2 = 48种。
答案: 48种。
(2026年3月第10题) 练习题: 6个人排成一排照相,其中甲、乙两人必须相邻,且丙不能站在排头,不同排法有( )种?
解析: ①先将甲乙捆绑看作一个整体,有2! = 2种内部排列。②此时有5个「元素」(甲乙整体+丙丁戊己)排列,总排列数5! = 120。③丙在排头的情况:丙固定排头,剩余4个「元素」(甲乙整体+丁戊己)排列,4! = 24,但甲乙内部有2! = 2种,所以丙在排头的情况有24×2=48种。④总方案数 = 120×2 - 48 = 240 - 48 = 192种。
答案: C(192种)。
2.2 组合——「顺序不重要」
组合数: 从n个不同元素中取出m个(m≤n)元素,不考虑顺序,称为组合。所有组合的个数称为组合数。
公式: C(n, m) = P(n, m) / m! = n! / [m! × (n-m)!]
重要性质:
· C(n, m) = C(n, n-m) —— 对称性
· C(n, 0) = C(n, n) = 1
· C(n, 1) = C(n, n-1) = n
(2024年3月第2题) 练习题: 从10个不同元素中选3个,有多少种选法?
答案: C(10, 3) = 10×9×8 / (3×2×1) = 120种。
2.3 有重复元素的全排列
公式: 如果n个元素中有k种不同的元素,每种元素分别有n₁, n₂, …, nₖ个(n₁+n₂+…+nₖ=n),那么全排列数为:
n! / (n₁! × n₂! × … × nₖ!)
(2024年6月判断第3题) 练习题: 一个袋子中有3个完全相同的红色小球、2个完全相同的蓝色小球。每次从中取出1个,再放回袋子,这样进行3次后,可能的颜色顺序有8种。
解析: 每次取出后放回,所以每次取球是独立的,有红和蓝2种可能。3次独立取球,共有2³ = 8种可能的颜色顺序。
答案:√。注意问题中「放回」是关键词,有放回时每次独立。
(2023年12月判断第2题) 练习题: 一个袋子中有3个完全相同的红色小球、2个完全相同的蓝色小球。每次从中取出1个,且不放回,这样进行3次后,将取出的小球依次排列,则可能的颜色顺序有7种。
解析: 不放回,从5个球中取3个。相当于从3红2蓝中选3个球排列。可能的红球数量可以是3、2、1。
· 3红:只有1种顺序(红红红)
· 2红1蓝:相当于3个位置选1个放蓝球,C(3,1)=3种
· 1红2蓝:相当于3个位置选1个放红球,C(3,1)=3种
总方案数 = 1 + 3 + 3 = 7种。
答案:√。
2.4 组合数学常见题型总结
题型1:相邻问题——捆绑法
将必须相邻的元素视为一个整体,先排整体,再排内部。(2026年3月第10题)
题型2:不相邻问题——插空法
先排其他元素,再在空隙中插入不能相邻的元素。
题型3:限定位置问题
先考虑特殊位置(如排头/排尾)的约束,再排其他元素。
题型4:至少/至多问题
用补集思想:总方案数 - 不满足条件的方案数。(2026年3月第1题、第4题)
题型5:有重复元素问题
用公式 n! / (n₁! × n₂! × … × nₖ!)
知识块③:杨辉三角与二项式定理——「数与形的完美结合」
3.1 杨辉三角的定义
杨辉三角(又称帕斯卡三角形) 是一种二项式系数的三角形排列。在中国南宋数学家杨辉1261年所著的《详解九章算法》一书中出现,是中国数学史上的一项伟大成就。
(2023年12月判断第3题) 判断题:杨辉三角,是二项式系数的一种三角形排列,在中国南宋数学家杨辉1261年所著的《详解九章算法》一书中出现,是中国数学史上的一项伟大成就。
答案:√。
3.2 杨辉三角的构造
第0行: 1
第1行: 1 1
第2行: 1 2 1
第3行: 1 3 3 1
第4行: 1 4 6 4 1
第5行: 1 5 10 10 5 1
构造规则: 第n行有n+1个数,每行首尾都是1,中间每个数等于它上方两数之和。
3.3 杨辉三角与组合数的关系
杨辉三角第n行第k个数(从0开始计数)等于 C(n, k),即组合数。
· 第0行:C(0,0) = 1
· 第1行:C(1,0)=1, C(1,1)=1
· 第2行:C(2,0)=1, C(2,1)=2, C(2,2)=1
· 第3行:C(3,0)=1, C(3,1)=3, C(3,2)=3, C(3,3)=1
3.4 杨辉三角的重要性质
(2025年12月判断第5题) 练习题: 在杨辉三角形中,第n行(从0开始计数,即第n行有n+1个数)的所有数字之和等于2ⁿ。
答案:√。第n行各数之和 = C(n,0) + C(n,1) + … + C(n,n) = 2ⁿ。
(2026年3月第2题) 练习题: 二项式(a+b)ⁿ展开式中各项的二项式系数之和等于2ⁿ。
答案:√。这就是杨辉三角第n行之和。
(2026年3月判断第2题) 练习题: 对于任意正整数n,二项式(a+b)ⁿ展开式中各项的二项式系数之和等于2ⁿ。
答案:√。
3.5 二项式定理
公式: (a+b)ⁿ = C(n,0) × aⁿ + C(n,1) × aⁿ⁻¹b + … + C(n,k) × aⁿ⁻ᵏbᵏ + … + C(n,n) × bⁿ
(2024年6月第12题) 练习题: (x+1)⁶的展开式中x³项的系数是?
解析: 根据二项式定理,x³项对应C(6,3) = 6×5×4/(3×2×1) = 20。
答案: 20。
知识块④:倍增法——「用指数级增长来加速查询」
4.1 倍增法的核心思想
倍增法(Binary Lifting) 是一种利用二进制分解来加速查询和计算的算法思想。核心是:将一次需要O(n)的查询,通过预处理,降为O(log n)甚至O(1)。
4.2 快速幂——「倍增法最经典的应用」
(2026年3月第3题) 练习题:快速幂算法的时间复杂度是多少?
答案: O(log n)。每次将指数减半,只需要log₂n次乘法。
// 快速幂计算 a^b % mod
long long fastPow(long long a, long long b, long long mod) {
long long res = 1;
while (b > 0) {
if (b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
4.3 RMQ(区间最值查询)——「ST表」
(2026年3月判断第7题) 练习题: 使用倍增法预处理区间最值问题时,预处理的时间复杂度为O(n log n),查询的时间复杂度为O(1)。
答案:√。ST表用倍增法预处理,每次查询O(1)即可得到区间最值。
ST表原理: st[i][k]表示从i开始长度为2ᵏ的区间的最值。
· 预处理:st[i][0] = a[i],st[i][k] = max(st[i][k-1], st[i+2ᵏ⁻¹][k-1])
· 查询[l, r]:k = floor(log₂(r-l+1)),答案为max(st[l][k], st[r-2ᵏ+1][k])
4.4 LCA(最近公共祖先)
倍增法求LCA: 预处理每个节点向上跳2ᵏ步的祖先节点,查询时先将两个节点跳到同一深度,再一起向上跳。
时间复杂度: 预处理O(n log n),单次查询O(log n)。
知识块⑤:代数与平面几何(初中数学部分)
5.1 一元一次方程
形式: ax + b = 0(a ≠ 0),解为 x = -b/a
(2023年12月判断第1题) 判断题: C++语言非常强大,可以用来求解方程的解。例如,如果变量x为double类型的变量,则执行语句 x * 2 - 4 = 0; 后,变量x的值会变为2.0。
答案:×。C++中 x * 2 - 4 = 0 不是赋值语句,这是一个表达式,不能用于赋值给x。正确的写法是 x = (0 + 4) / 2;。
5.2 二元一次方程
形式: 两个方程组成的方程组,如:
ax + by = c
dx + ey = f
解法: 代入消元法、加减消元法。
5.3 平面几何基本图形面积
三角形面积: S = 底 × 高 / 2
(2023年12月判断第7题) 练习题: 已知int类型的变量a、b和h中分别存储着一个梯形的顶边长、底边长和高,则这个梯形的面积可以通过表达式 (a+b)*h/2 求得。
答案:√。梯形面积公式为 (上底+下底)×高/2。
长方形面积: S = 长 × 宽
圆形面积: S = π × r²
勾股定理(2023年12月判断第4题): 直角三角形两直角边a、b的平方和等于斜边c的平方,即 a² + b² = c²。
(2024年6月判断第4题) 练习题: 已知int类型的变量a和b中分别存储着一个直角三角形的两条直角边的长度,则斜边的长度可以通过表达式 sqrt(a * a + b * b) 求得。
答案:√。勾股定理,sqrt函数计算平方根。
(2026年6月第8题) 练习题: 判断点P(x, y)是否在以原点为圆心、半径为5的圆内或圆上,正确的判断条件是?
答案:x * x + y * y <= 25。到原点距离的平方 ≤ 半径的平方。
(2026年3月第14题) 三点共线判定: 给定三个点,通过斜率相等或向量叉积为0来判断是否共线。
知识块⑥:图论算法及综合应用——「八级的绝对核心」
6.1 最小生成树(MST)
定义: 在一个连通无向带权图中,找出一个包含所有顶点的树形子图,使得所有边权之和最小。
(2024年6月第9题) 练习题: 某无向带权图有边(1,2,4)、(1,3,2)、(2,3,1)、(2,4,5)、(3,4,8)、(3,5,10)、(4,5,2)。该图最小生成树的总权值为?
解析: 用Kruskal算法,按边权从小到大选择:
· 选(2,3)权1 → 选(1,3)权2 → 选(4,5)权2 → 选(1,2)权4(但(1,2)和(2,3)(1,3)连通了,形成环,跳过)→ 选(2,4)权5
总权值 = 1 + 2 + 2 + 5 = 10。
答案: D(10)。
算法1:Kruskal算法——「选边法」
核心思想: 将所有边按权值从小到大排序,依次选择不会形成环的边加入MST,直到选够n-1条边。
(2025年12月第15题) 练习题: 对连通无向图执行Kruskal算法。已按边权从小到大依次扫描到某条边e=(u,v)。此时在已经构建的部分MST结构中,(u,v)已在同一连通块内。关于边e的处理,下列说法正确的是?
解析: 如果(u,v)已在同一连通块内,再选这条边会形成环,所以一定不能选入MST(在此扫描顺序下)。
答案: B。
// Kruskal算法(并查集实现)
struct Edge { int u, v, w; };
vector<Edge> edges;
vector<int> parent(n+1);
int find(int x) {
return parent[x] == x ? x : parent[x] = find(parent[x]);
}
int kruskal() {
sort(edges.begin(), edges.end(), [](Edge a, Edge b){ return a.w < b.w; });
iota(parent.begin(), parent.end(), 0);
int total = 0, cnt = 0;
for (auto& e : edges) {
int ru = find(e.u), rv = find(e.v);
if (ru != rv) {
parent[ru] = rv;
total += e.w;
if (++cnt == n-1) break;
}
}
return total;
}
(2025年9月第14题) 练习题: Prim算法中松弛操作的条件是?
答案:graph[u][v] != 0 && key[v] > graph[u][v]。当u和v之间有边且v的当前key值大于边的权值时,更新key值。
算法2:Prim算法——「选点法」
核心思想: 从一个顶点开始,每次选择与当前已选顶点集合相连的最小权值边,将新顶点加入集合,直到所有顶点都被选中。
Kruskal vs Prim:
策略
Kruskal算法:选边(按权值从小到大)
Prim算法:选点(从已选集合向外扩展)
数据结构
Kruskal算法:并查集
Prim算法:堆/优先队列
时间复杂度
Kruskal算法:O(E log E)
Prim算法:O(E log V)(堆优化)/ O(V²)(朴素)
适用场景
Kruskal算法:稀疏图(E较小)
Prim算法:稠密图(V较小)
(2026年3月判断第9题) 练习题: Kruskal算法和Prim算法都可以用来求解最小生成树,且这两者的贪心策略无论在任何连通无向图上求得的最小生成树总边权和必定相同。
答案:×。如果图中有多条权值相同的边,不同的MST算法可能选择不同的边,但总边权值相同。但「必定相同」的说法过于绝对——如果图中有相同权值的边,不同的实现可能选出不同的边集,但总权值一定相同。这道题需要注意:总边权和一定相同,因为最小生成树的总权值是唯一的,但具体选哪条边可能不同。
(2026年3月判断第4题) 练习题: 若一个无向图的最小生成树唯一,则图中所有边权必定各不相同。
答案:×。边权各不相同是MST唯一的充分条件,但不是必要条件。即使有相同权值的边,MST也可能唯一。
(2026年3月判断第8题) 练习题: 如果将一个连通无向图G₁中所有边的权值都统一增加同一个正整数常数C,形成图G₂。则G₁的最小生成树中每条边在G₂中对应的边组成的树,一定是G₂的最小生成树。
答案:×。所有边权统一增加C后,原来的MST不一定是新的MST。因为MST的选边依据是边权大小比较,统一增加后,原来权值较大的边和权值较小的边之间的相对差距不变,所以MST结构不变。但注意:如果图中有两条边权值相同,增加相同常数后仍然相等,所以MST不变。但如果增加的是同一个常数,其实所有边的相对大小关系不变,所以MST结构不变。这道题实际上需要仔细分析——如果所有边权统一增加同一个正数,MST结构不变,因为MST只依赖于边的相对大小顺序。但答案为什么是×?因为题目说的是「统一增加同一个正整数常数C」,如果C是负数或者考虑边的权值可能为0的情况,情况可能不同。实际上在正权图中,统一增加同一正数,MST结构保持不变。但命题的陷阱在于「增加」后,如果有两条边权值相同,原本可以选其中任意一条,但增加后仍然相等,所以MST结构保持不变。这道题的正确答案存在争议,建议以官方答案为准。
6.2 最短路径——「Dijkstra算法与Floyd算法」
算法1:Dijkstra算法——「单源最短路」
(2026年6月第10题) 练习题: 有向非负权图边为1→2(3)、2→4(4)、1→3(10)、3→4(1)、2→3(2)。使用Dijkstra算法从1号顶点出发到4号顶点的最短距离为?
解析:
· 从1出发:dist[1]=0,dist[2]=3,dist[3]=10,dist[4]=∞
· 选最小未访问节点2:通过2更新dist[3]=min(10, 3+2)=5,dist[4]=min(∞, 3+4)=7
· 选最小未访问节点3:通过3更新dist[4]=min(7, 5+1)=6
· 选最小未访问节点4:结束
答案: A(6)。
Dijkstra算法的时间复杂度(2026年3月第6题):
· 朴素实现:O(V²)
· 优先队列(小根堆)优化:O((V+E) log V)(2025年9月第11题)
Dijkstra算法的重要限制:不能处理负权边。(2026年3月第9题)
(2026年3月第9题) 练习题: 关于图论中的最短路径算法,下列说法中严格正确的是?
· A. Dijkstra算法能够高效处理包含负权边的有向图 → ×
· B. Floyd算法可以求出任意两点间的最短路径,且允许图中存在负权边(但不能有负权环) → √
· C. 单源最短路径算法无法用于无向图 → ×,无向图可用于Dijkstra
· D. Dijkstra算法的每一步必定从当前未访问的节点中,选取距离起始点最远的节点进行松弛操作 → ×,是选取距离最近的节点
(2025年12月判断第6题) 练习题: 使用二叉堆优化的Dijkstra最短路算法,在某些特殊情况下时间复杂度不如朴素实现的O(V²)。
答案:√。当图是稠密图(E接近V²)时,堆优化的Dijkstra为O((V+V²)log V) = O(V² log V),反而不如朴素实现的O(V²)。
算法2:Floyd算法——「全源最短路」
(2026年3月第11题) 练习题:Floyd算法中,横线处应填入?
void floyd(int n, int dist[][MAXN]) {
for (int k = 0; k < n; k++)
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if ( ______ )
dist[i][j] = dist[i][k] + dist[k][j];
}
答案:dist[i][k] != INF && dist[k][j] != INF && dist[i][k] + dist[k][j] < dist[i][j]。需要同时满足:i到k有路径、k到j有路径、且通过k中转更短。
Floyd算法时间复杂度: O(V³)
(2026年3月第8题) 练习题: 在使用Floyd算法求任意两点间最短路径时,时间复杂度为O(V³)。若在某次算法执行前,已经用Dijkstra算法正确求出了所有点对的最短路并存入了dist数组。如果此时继续对该dist数组执行一次完整的Floyd算法过程(无任何提前终止),执行完毕后dist数组内的值( )。
答案: B(不会发生改变)。因为dist数组中已经是正确的最短路径,Floyd的松弛操作不会产生更小的值。
6.3 欧拉回路
(2026年3月判断第6题) 练习题: 若一个图中所有顶点的度数为偶数,则一定存在欧拉回路。
答案:×。还需要图是连通的。对于无向图,欧拉回路存在的充要条件是:所有顶点度数均为偶数,且图是连通的(或所有边属于同一个连通分量)。
知识块⑦:算法的时间和空间效率分析
7.1 时间复杂度分析
大O表示法: 描述算法运行时间随输入规模增长的趋势,忽略常数因子和低阶项。
常见时间复杂度比较:
O(1) < O(log n) < O(n) < O(n log n) < O(n²) < O(n³) < O(2ⁿ)
7.2 各类算法的时间复杂度
(2025年12月判断第3题) 练习题: 快速排序和归并排序的平均时间复杂度都是O(n log n),但快速排序是不稳定的排序算法,归并排序是稳定的排序算法。
答案:√。
(2025年12月判断第8题) 练习题: 快速排序在最坏情况下的时间复杂度为O(n log n),可以通过随机化选择基准值(pivot)的方法完全避免退化。
答案:×。快速排序最坏情况时间复杂度为O(n²)。随机化选择基准值可以降低退化为最坏情况的概率,但无法完全避免。
(2026年6月第11题) 练习题: 下列代码片段的时间复杂度为?
for (int i = 1; i <= n; i++)
for (int j = 1; j * j <= n; j++)
s += i + j;
解析: 外层循环n次,内层循环√n次,总时间复杂度为O(n√n)。
答案: C(O(n√n))。
时间复杂度速查表:
冒泡排序
最好情况:O(n)
平均情况:O(n²)
最坏情况:O(n²)
插入排序
最好情况:O(n)
平均情况:O(n²)
最坏情况:O(n²)
选择排序
最好情况:O(n²)
平均情况:O(n²)
最坏情况:O(n²)
快速排序
最好情况:O(n log n)
平均情况:O(n log n)
最坏情况:O(n²)
归并排序
最好情况:O(n log n)
平均情况:O(n log n)
最坏情况:O(n log n)
堆排序
最好情况:O(n log n)
平均情况:O(n log n)
最坏情况:O(n log n)
二分查找
最好情况:O(log n)
平均情况:O(log n)
最坏情况:O(log n)
二叉树查找
最好情况:O(log n)
平均情况:O(log n)
最坏情况:O(n)
DFS/BFS(邻接表)
最好情况:O(V+E)
平均情况:O(V+E)
最坏情况:O(V+E)
埃氏筛法
最好情况:O(n log log n)
平均情况:O(n log log n)
最坏情况:O(n log log n)
快速幂
最好情况:O(log n)
平均情况:O(log n)
最坏情况:O(log n)
(2024年6月第13题) 练习题: 埃拉托斯特尼筛法(埃氏筛法)的时间复杂度是?
答案: O(n log log n)。
(2024年6月第14题) 练习题: 辗转相除法(欧几里得算法)求最大公约数的最差时间复杂度是?
答案: O(log n)。
7.3 空间复杂度分析
空间复杂度是指算法在运行过程中所占用的存储空间大小,通常也用大O表示法。
常见空间复杂度:
· O(1):只需要常数个额外变量
· O(n):需要线性大小的额外空间(如数组)
· O(n²):需要二维数组大小的空间
· O(n log n):如归并排序的临时数组、ST表
知识块⑧:算法优化——「让程序跑得更快」
8.1 数学知识优化
例1:等差数列求和
// 优化前:循环求和 O(n)
int sum = 0;
for (int i = 1; i <= n; i++) sum += i;
// 优化后:数学公式 O(1)
int sum = n * (n + 1) / 2;
例2:快速幂(知识块④已介绍)
8.2 时间与空间的权衡
典型场景:
· 滚动数组: 用O(n)空间代替O(n²)空间,但时间不变(如DP优化)
· 哈希表: 用空间换时间,查找O(1)但需要额外存储
· 记忆化搜索: 用数组存储中间结果,避免重复计算
8.3 不同算法求解同一问题的差异分析
例: 求最短路径
· BFS(无权图): O(V+E),但只能处理无权图
· Dijkstra(非负权图): O((V+E)log V),处理非负权图
· Floyd(任意图,无负环): O(V³),求所有点对最短路
· Bellman-Ford(任意图,含负权): O(VE),可检测负环
(2025年9月第8题) 练习题: 贪心法和动态规划都是解决最优化问题的方法,下列说法正确的是?
解析: 贪心法每一步选择局部最优,不一定得到全局最优;动态规划通过子问题的最优解构建全局最优解。
EXAMS 03
历年真题考点总览
基于2023年12月~2026年6月共10套GESP C++八级真题的考点统计分析。
各知识块在10套真题中的出现频率
排列组合与计数原理
出现套数:10/10套
覆盖率:100%
每套平均题量:4~5题
重要程度:⭐⭐⭐⭐⭐
图论算法(MST+最短路)
出现套数:10/10套
覆盖率:100%
每套平均题量:3~4题
重要程度:⭐⭐⭐⭐⭐
算法复杂度分析
出现套数:10/10套
覆盖率:100%
每套平均题量:3~4题
重要程度:⭐⭐⭐⭐⭐
杨辉三角与二项式定理
出现套数:7/10套
覆盖率:70%
每套平均题量:1~2题
重要程度:⭐⭐⭐
代数与平面几何
出现套数:6/10套
覆盖率:60%
每套平均题量:1~2题
重要程度:⭐⭐⭐
倍增法
出现套数:4/10套
覆盖率:40%
每套平均题量:1题
重要程度:⭐⭐
考点分布总体分析
选择题考点分布规律
第1~2题
常见考点:排列组合(排队方案、选法计数)
说明:几乎每套前两题必有
第3~4题
常见考点:排列组合 / 图论基础 / 算法复杂度
说明:交替出现
第5~6题
常见考点:杨辉三角性质 / 图论算法复杂度
说明:二项式系数之和、Dijkstra堆优化
第7~8题
常见考点:图论性质 / 平面几何 / 排序算法
说明:最短路径边数、几何判断
第9~10题
常见考点:最小生成树计算 / Dijkstra最短路计算
说明:高频考点
第11~12题
常见考点:时间复杂度分析 / Floyd算法代码填空
说明:代码填空常考
第13~14题
常见考点:算法复杂度 / 图论算法代码填空 / 几何
说明:代码填空
第15题
常见考点:图论综合 / 程序输出分析
说明:最后一题通常较难
判断题考点分布规律
排列组合
平均题量:2~3题
常见陷阱:相邻/不相邻问题、有放回/无放回、有重复元素
图论算法
平均题量:2~3题
常见陷阱:MST唯一性、欧拉回路条件、Dijkstra优化、Kruskal/Prim比较
C++语法/数学
平均题量:1~2题
常见陷阱:赋值语句、static成员、const引用、析构函数、运算符重载
排序/复杂度
平均题量:1~2题
常见陷阱:快排最坏O(n²)、随机化不能完全避免退化
哈希表/动态规划
平均题量:1题
常见陷阱:冲突不可避免、DP递推与递归复杂度比较
TRAPS 04
判断题高频陷阱汇总
第1名:排列组合相关(出现约12次)
「有5个不同的小球,装入3个不同的盒子,每个盒子至少1个,共有150种装法。」(2024年9月判断第1题)
答案:×。这类问题需要仔细计算,涉及斯特林数或容斥原理。
「一个袋子中有3个完全相同的红色小球、2个完全相同的蓝色小球。每次从中取出1个,再放回袋子,这样进行3次后,可能的颜色顺序有8种。」(2024年6月判断第3题)
答案:√。有放回,每次独立,2³=8。
「一个袋子中有3个完全相同的红色小球、2个完全相同的蓝色小球。每次从中取出1个,且不放回,这样进行3次后,将取出的小球依次排列,则可能的颜色顺序有7种。」(2023年12月判断第2题)
答案:√。无放回,分情况讨论得7种。
「ABC三个同学排成一排,有6种排法。」(这个简单,但容易在复杂问题时出错)
答案:√。3! = 6。
第2名:图论算法相关(出现约10次)
「若一个图中所有顶点的度数为偶数,则一定存在欧拉回路。」(2026年3月判断第6题)
答案:×。还需要图是连通的。
「若一个无向图的最小生成树唯一,则图中所有边权必定各不相同。」(2026年3月判断第4题)
答案:×。边权各不相同是充分条件,不是必要条件。
「Kruskal算法和Prim算法求得的MST总边权和必定相同。」(2026年3月判断第9题)
答案:√。MST的总权值是唯一的,但具体选的边可能不同。
「如果将一个连通无向图G₁中所有边的权值都统一增加同一个正整数常数C,形成图G₂。则G₁的最小生成树中每条边在G₂中对应的边组成的树,一定是G₂的最小生成树。」(2026年3月判断第8题)
答案:×。所有边权统一增加后,边的相对大小关系不变,MST结构不变。但注意增加的C如果是正数,MST结构保持不变。题目中「一定是」这个绝对化表述就是陷阱,实际上MST结构不变。
「使用二叉堆优化的Dijkstra最短路算法,在某些特殊情况下时间复杂度不如朴素实现的O(V²)。」(2025年12月判断第6题)
答案:√。稠密图(E≈V²)时,堆优化为O(V² log V),朴素为O(V²)。
第3名:排序算法与复杂度分析相关(出现约8次)
「快速排序在最坏情况下的时间复杂度为O(n log n)。」(2025年6月判断第3题)
答案:×。最坏情况是O(n²)。
「快速排序在最坏情况下的时间复杂度为O(n log n),可以通过随机化选择基准值(pivot)的方法完全避免退化。」(2025年12月判断第8题)
答案:×。随机化只能降低概率,不能完全避免。
「快速排序和归并排序的平均时间复杂度都是O(n log n),但快速排序是不稳定的排序算法,归并排序是稳定的排序算法。」(2025年12月判断第3题)
答案:√。
「使用快速排序对n个元素进行排序时,无论最好、最坏还是平均情况,时间复杂度均为O(n log n)。」(2026年3月判断第5题)
答案:×。最坏情况O(n²)。
第4名:C++语言特性相关(出现约7次)
「C++语言中,可以为同一个类定义多个析构函数。」(2024年6月判断第7题)
答案:×。析构函数不能重载,只能有一个。
「在C++语言中,一个类可以拥有多个构造函数,也可以拥有多个析构函数。」(2025年12月判断第9题)
答案:×。可以有多个构造函数(重载),但只能有一个析构函数。
「在C++中,若结构体中包含一个static成员变量,则该变量的存储空间属于结构体对象的一部分。」(2026年3月判断第1题)
答案:×。static成员变量被所有对象共享,不属于任何一个对象,在类外单独存储。
「在C++中,若函数参数类型为const int &,则该参数既可以绑定左值,也可以绑定右值。」(2026年3月判断第3题)
答案:√。const引用可以绑定到右值(临时对象)。
第5名:杨辉三角/二项式定理相关(出现约6次)
「在杨辉三角形中,第n行(从0开始计数,即第n行有n+1个数)的所有数字之和等于2ⁿ。」(2025年12月判断第5题)
答案:√。
「对于任意正整数n,二项式(a+b)ⁿ展开式中各项的二项式系数之和等于2ⁿ。」(2026年3月判断第2题)
答案:√。
第6名:动态规划/哈希表相关(出现约5次)
「在动态规划问题中,『状态转移方程+递推』和『递归+记忆化搜索』通常是解决同一问题的两种不同实现方式,它们的时间复杂度总是相同的。」(2026年3月判断第10题)
答案:×。大部分情况下时间复杂度相同,但有些情况下递推实现可以避免递归的额外开销,且递归可能有栈溢出风险。
「无论哈希表采用何种方式解决冲突,只要管理的元素足够多,都无法避免冲突。」(2025年6月判断第7题)
答案:√。
TEMPLATES 05
编程题全分析
各套真题编程题一览
2023年12月
编程题1:奖品分配
考点:组合数学、DP
编程题2:礼物分配
考点:图论、树形DP
2024年3月
编程题1:公倍数统计
考点:计数、枚举优化
编程题2:接竹竿
考点:队列模拟、游戏
2024年6月
编程题1:最远点对
考点:树的DFS、树形DP
编程题2:空间跳跃
考点:Dijkstra最短路、建图
2024年9月
编程题1:选择客栈
考点:组合计数、枚举
编程题2:最小生成树
考点:Kruskal/MST
2024年12月
编程题1:置换游戏
考点:排列、模拟
编程题2:邮递员
考点:Dijkstra最短路
2025年3月
编程题1:欧拉路
考点:欧拉回路/欧拉路径
编程题2:消息传递
考点:Dijkstra、图论建模
2025年6月
编程题1:树上路径
考点:树的DFS、路径统计
编程题2:删边最小生成树
考点:Kruskal/MST、并查集
2025年9月
编程题1:最短距离
考点:图论最短路、互质建图
编程题2:最小生成树删边
考点:Kruskal/MST、树链剖分
2025年12月
编程题1:猫和老鼠
考点:图论、BFS最短路
编程题2:学习小组
考点:DP、分组优化
2026年3月
编程题1:消息查找
考点:图论最短路、推理解密
编程题2:子图最短路
考点:Floyd算法、枚举优化
2026年6月
编程题1:奖品分配
考点:组合计数、DP
编程题2:子图连通性
考点:图论、连通性判断
编程题高频考点总结
图论算法(MST+最短路)
出现次数:约8次
占比:约73%
说明:Dijkstra最短路、Kruskal/Prim最小生成树、Floyd、BFS最短路
动态规划
出现次数:约4次
占比:约36%
说明:树形DP、分组DP、组合计数DP
组合数学/计数
出现次数:约3次
占比:约27%
说明:排列组合、枚举计数
树的遍历(DFS)
出现次数:约3次
占比:约27%
说明:树形DFS、路径统计、最远点对
编程必会模板
模板1:Dijkstra算法(单源最短路径,堆优化)
const int INF = 0x3f3f3f3f;
vector<pair<int, int>> adj[MAXN]; // (邻接点, 边权)
int dist[MAXN];
bool visited[MAXN];
void dijkstra(int start) {
fill(dist, dist + MAXN, INF);
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});
}
}
}
}
模板2:Kruskal算法(最小生成树)
struct Edge { int u, v, w; };
vector<Edge> edges;
int parent[MAXN];
int find(int x) {
return parent[x] == x ? x : parent[x] = find(parent[x]);
}
int kruskal(int n) {
sort(edges.begin(), edges.end(), [](Edge a, Edge b){ return a.w < b.w; });
iota(parent, parent + n + 1, 0);
int total = 0, cnt = 0;
for (auto& e : edges) {
int ru = find(e.u), rv = find(e.v);
if (ru != rv) {
parent[ru] = rv;
total += e.w;
if (++cnt == n - 1) break;
}
}
return total;
}
模板3:Prim算法(最小生成树,朴素实现)
const int INF = 0x3f3f3f3f;
int graph[MAXN][MAXN]; // 邻接矩阵
int key[MAXN];
bool inMST[MAXN];
int prim(int n) {
fill(key, key + n, INF);
fill(inMST, inMST + n, false);
key[0] = 0;
int total = 0;
for (int i = 0; i < n; i++) {
int u = -1, minKey = INF;
for (int v = 0; v < n; v++)
if (!inMST[v] && key[v] < minKey)
minKey = key[v], u = v;
if (u == -1) break;
inMST[u] = true;
total += key[u];
for (int v = 0; v < n; v++)
if (graph[u][v] != 0 && !inMST[v] && graph[u][v] < key[v])
key[v] = graph[u][v];
}
return total;
}
模板4:Floyd算法(全源最短路径)
const int INF = 0x3f3f3f3f;
int dist[MAXN][MAXN];
void floyd(int n) {
for (int k = 0; k < n; k++)
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (dist[i][k] != INF && dist[k][j] != INF
&& dist[i][k] + dist[k][j] < dist[i][j])
dist[i][j] = dist[i][k] + dist[k][j];
}
模板5:快速幂
long long fastPow(long long a, long long b, long long mod) {
long long res = 1;
while (b > 0) {
if (b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
模板6:组合数计算
// 递推法(杨辉三角)求组合数 C(n, m)
long long C[MAXN][MAXN];
void initComb(int n) {
for (int i = 0; i <= n; i++) {
C[i][0] = C[i][i] = 1;
for (int j = 1; j < i; j++)
C[i][j] = C[i-1][j-1] + C[i-1][j];
}
}
模板7:ST表(倍增法求区间最值)
const int LOG = 20;
int st[MAXN][LOG]; // st[i][k] 表示从i开始长度为2^k的区间最大值
void buildST(int a[], int n) {
for (int i = 0; i < n; i++) st[i][0] = a[i];
for (int k = 1; (1 << k) <= n; k++)
for (int i = 0; i + (1 << k) - 1 < n; i++)
st[i][k] = max(st[i][k-1], st[i + (1 << (k-1))][k-1]);
}
int query(int l, int r) {
int k = log2(r - l + 1);
return max(st[l][k], st[r - (1 << k) + 1][k]);
}
模板8:图的DFS遍历(判断连通性)
bool visited[MAXN];
vector<int> adj[MAXN];
void dfs(int u) {
visited[u] = true;
for (int v : adj[u]) {
if (!visited[v]) dfs(v);
}
}
// 判断连通性:从任意一个顶点开始DFS,看是否所有顶点都被访问
bool isConnected(int n) {
dfs(1);
for (int i = 1; i <= n; i++)
if (!visited[i]) return false;
return true;
}
PLAN 06
复习建议与考场技巧
复习时间规划(距考试3周)
第1周:基础打牢——「先把组合数学和图论算法吃透」
第1天
学习内容:排列组合(一)
重点:加法原理与乘法原理、排列数公式、组合数公式、相邻/不相邻问题
第2天
学习内容:排列组合(二)
重点:有重复元素排列、至少/至多问题、捆绑法、插空法
第3天
学习内容:杨辉三角与二项式定理
重点:杨辉三角构造、性质、与组合数的关系、二项式展开
第4天
学习内容:图论算法(一)
重点:最小生成树:Kruskal算法(并查集)、Prim算法(选点法)
第5天
学习内容:图论算法(二)
重点:最短路径:Dijkstra算法(堆优化)、Floyd算法
第6天
学习内容:算法复杂度分析
重点:各类排序/查找/图遍历/DP复杂度、时间复杂度速查表
第7天
学习内容:倍增法 + 代数与平面几何
重点:快速幂、ST表、三角形面积、勾股定理、三点共线
第2周:真题训练——「把知识变成分数」
第1天
学习内容:做2023年12月真题,限时180分钟完成
第2天
学习内容:对照分析错题,重点复习排列组合和图论算法
第3天
学习内容:做2024年两套真题(3月+6月),限时完成
第4天
学习内容:分析错题,重点复习MST计算和Dijkstra最短路
第5天
学习内容:做2025年两套真题(3月+6月),限时完成
第6天
学习内容:编程题专项训练:Dijkstra + Kruskal
第7天
学习内容:做2026年最新真题,检验学习效果
第3周:查漏补缺——「哪里不会补哪里」
· 复习「判断题高频陷阱」部分——排列组合的陷阱最多,一定要区分有放回/无放回
· 熟背编程模板(8个必会模板)——考试时直接套用
· 限时模拟一套完整试卷(180分钟)
· 把之前的错题再过一遍
考场技巧
做题顺序建议
单选题
题量:15题
建议用时:约40分钟
策略:不会的先跳过,不要在一道题上花超过3分钟
判断题
题量:10题
建议用时:约20分钟
策略:注意「一定」「总是」「所有」这类绝对化词语
编程题
题量:2题
建议用时:约100分钟
策略:先读懂题目,再动手写代码,最后15分钟检查
单选题常见陷阱提醒
陷阱1:排列组合
· 区分「排列」和「组合」:顺序重要用排列,顺序不重要用组合
· 区分「有放回」和「无放回」:有放回每次独立,无放回每次减少
· 区分「相邻」和「不相邻」:相邻用捆绑法,不相邻用插空法
· 「至少」问题:用补集思想,总方案数减去不满足条件的方案数
陷阱2:图论算法
· Dijkstra不能处理负权边
· Floyd允许负权边,但不能有负权环
· Kruskal选边法,边权从小到大,用并查集判环
· Prim选点法,从已选集合向外扩展最小边
· MST唯一性:边权完全相同是充分条件,不是必要条件
陷阱3:算法复杂度
· 快速排序最坏O(n²),不是O(n log n)
· 随机化不能完全避免退化
· 邻接表BFS/DFS = O(V+E),邻接矩阵BFS/DFS = O(V²)
· 堆优化Dijkstra:稠密图下不如朴素O(V²)
陷阱4:C++语法
· 析构函数只能有一个,不能重载
· static成员变量不属于对象,被所有对象共享
· const引用可以绑定右值
· pow()返回值类型为double
· sqrt()返回值类型为double
编程题检查清单
□ 图的存储方式选对了吗?(稀疏图用邻接表,稠密图用邻接矩阵)
□ Dijkstra算法中dist数组初始化了吗?(初始化为INF)
□ Kruskal算法中并查集的find函数写对了吗?(路径压缩)
□ 排列组合题中C(n,m)的递推正确吗?n和m的范围?
□ Floyd算法中三层循环的顺序对吗?(k在最外层)
□ 快速幂的模运算正确吗?
□ 数据类型选对了吗?(int不够用long long?)
□ 有没有漏掉 return 0;?
THE END ∞
写在最后
祝各位考生在GESP C++八级考试中取得好成绩!
八级是GESP认证的最高级别,是从「算法学习者」到「算法设计者」的跨越。从六级「树结构与基础DP」到七级「图论与复杂DP」,再到八级「组合数学+图论算法综合应用+算法分析与优化」,知识体系的广度和深度都达到了顶点。
八级的三大核心:
1. 排列组合与计数原理——选择题和判断题的绝对主力,约31%
2. 图论算法综合应用(最小生成树+最短路径)——选择题和编程题的核心,约25%+编程题
3. 算法复杂度分析与优化——贯穿所有题目,约15%
八级考试180分钟,与六级、七级相同。但题目难度是全部级别中最高的,特别是图论算法题的编程题,不仅要求实现算法,还要求根据数据规模选择合适的算法和优化方案。但只要把排列组合的基本题型、Dijkstra最短路、Kruskal最小生成树、Floyd算法、各类算法复杂度这几个核心内容练熟,就能顺利通过考试!
加油!
加油
DOMIAI
我是 DOMIAI,帮更多孩子成为 AI 时代原住民。
如果有收获,欢迎点赞、在看、转发三连,我们下篇见。
THANKS FOR READING