#include <iostream> 就像把厨具柜搬进来(引入工具箱),main() 是厨房操作台(程序起点),cin>>=把菜拿进厨房、cout<<=把菜端上桌。每句结尾都要加分号,就像说话要加句号!
CCF GESP 编程能力等级认证(C++)· 知识点手册
依据《CCF 编程能力等级认证 C++&Python 认证标准》整理,C++ 考题以 C++11 标准为准。GESP 由中国计算机学会主办,C++ 编程认证共一至八级,从 C++ 入门到图论与算法优化逐级递进。每个知识点均配有详细讲解、C++ 代码示例、易错点提示,并配模拟题讲解;难懂处附适合小学生、初中生的生活化例子。
考纲总览
一、等级逻辑关系总览
《CCF 编程能力等级认证 C++&Python 认证标准》由中国计算机学会(CCF)制定,认证划分为一至八级,' '为学习者提供编程能力水平的证明。考试采用上机考试,题型为单选题(15 道 × 2 分)+ 判断题(10 道 × 2 分)+ 编程题(2 道 × 25 分),' '1-4 级考试时间 120 分钟,5-8 级 180 分钟。
八级内容呈现明显的螺旋递进:基础语法 → 函数与算法 → 数据结构与搜索 → 动态规划与图论 → 综合与优化。
二、各级别核心能力要求总览
[['1', '一级 · 编程入门', '基础语法与图形编程', '了解计算机构成与开发环境,能独立完成简单功能的顺序、分支、循环程序'], ['2', '二级 · 基础拓展', '存储网络知识与函数', '掌握类型转换、数学库函数,能独立完成多分支与循环嵌套程序'], ['3', '三级 · 数据与算法起步', '编码、数组、字符串、枚举模拟', '掌握编码、进制、位运算,能用枚举法、模拟法解决实际问题'], ['4', '四级 · 函数与算法进阶', '函数、排序、递推、文件异常', '掌握模块化编程、递推、排序算法,理解复杂度,会文件读写'], ['5', '五级 · 数论与高效算法', '数论、二分、递归、分治贪心', '掌握数论与二分、递归、分治、贪心算法,能选择合适算法'], ['6', '六级 · 数据结构与搜索', '树、搜索、背包、类、栈队列', '掌握树与搜索算法、简单动态规划、面向对象,会使用栈队列'], ['7', '七级 · 动态规划与图论', '复杂DP、图、哈希', '掌握复杂动态规划、图论基础算法与哈希表'], ['8', '八级 · 综合与优化', '组合、图论综合、优化', '掌握组合数学、图论综合应用与算法优化']]三、使用说明与备考建议总览
- 本手册按 GESP 8 级顺序将知识点拆分为卡片,左侧导航可快速跳转;
- 每个知识点包含概念讲解 → 代码示例 → 易错点 → 模拟题的完整闭环,难懂处配“生活小例子”;
- 代码块右上角有“复制”按钮,例题答案可点击“查看答案”展开;
- 顶部可一键切换 电子学会 Python / 电子学会 C++ / GESP Python / GESP C++ 四本手册。
一级 · 编程入门
1.1 计算机基础与编程环境第一级知识
① 计算机的组成与历史
- 硬件:CPU(大脑)、内存、I/O 设备(键盘鼠标/显示器);
- 软件:操作系统(Windows、Linux)、应用软件;
- 历史:电子管 → 晶体管 → 集成电路 → 大规模集成电路。
② 集成开发环境(Dev C++ 等)
GESP 一级要求会用 IDE:创建文件、编辑、保存、编译、运行、调试。C++ 源文件后缀是 .cpp。
error 和警告 warning 要区分:error 必须改。1.2 程序框架与基本语句第一级知识
① 程序框架
#include <iostream>
using namespace std;
int main() {
// 程序从这里开始
return 0;
}② cin / cout / 赋值
int a, b;
cin >> a >> b; // 输入
cout << a + b << endl; // 输出 + 换行
a = 10; // 赋值语句
a++; // 自增 a = a + 1
a--; // 自减 a = a - 1>> 是输入方向、<< 是输出方向。1.3 标识符、常量与变量第一级知识
① 标识符与关键字
- 标识符:变量/函数名,由字母、数字、下划线组成,不能以数字开头,区分大小写;
- 关键字:int、if、for、while 等,不能作变量名。
② 常量与变量
const int MAXN = 100; // 常量(不可改)
int age = 10; // 变量:声明 + 初始化
age = 11; // 修改变量的值
int n; // 先声明(不初始化)变量像贴了名字的储物箱:int age 先造一个能装整数的箱子并起名 age,= 10 往里放 10。常量 const 像上了锁的保险箱,放进去就不能改。
1.4 基本数据类型第一级知识
① 四种基本类型
| 类型 | 占位 | 含义 | 示例 | |||||||
|---|---|---|---|---|---|---|---|---|---|---|
| i | n | t | ||||||||
| % | d | |||||||||
| 整 | 数 | |||||||||
| i | n | t | a | = | 1 | 0 | ; |
② printf / scanf 基础
int a = 10;
double b = 3.14;
char c = 'A';
printf("%d %.2lf %c\n", a, b, c);
scanf("%d", &a); // 注意 & 取地址&。1.5 基本运算第一级知识
① 算术运算
int a = 7, b = 2;
cout << a + b << " " << a - b << " "; // 9 5
cout << a * b << " " << a / b << " "; // 14 3(整数除法)
cout << a % b << endl; // 1(取余)② 关系运算与逻辑运算
| 类别 | 符号 | 例子 | 结果 | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 关 | 系 | |||||||||||||
| > | < | > | = | < | = | = | = | ! | = | |||||
| 3 | > | 2 | ||||||||||||
| t | r | u | e |
==,赋值用 =,别混!1.6 顺序、分支与循环第一级知识
① if / if-else / switch
int s = 85;
if (s >= 90) cout << "A";
else if (s >= 80) cout << "B";
else cout << "C";
// switch
int d = 2;
switch (d) {
case 1: cout << "一"; break;
case 2: cout << "二"; break;
default: cout << "其他";
}② for / while / do-while
int sum = 0;
for (int i = 1; i <= 100; i++) sum += i; // 5050
int j = 0;
while (j < 5) { cout << j; j++; } // 01234
int k = 0;
do { cout << k; k++; } while (k < 3); // 012 至少执行一次break 跳出、continue 跳过本次;② do-while 至少执行一次;③ switch 的 case 记得 break。一级模拟题第一级知识
以下例题贴合 GESP 一级考点(环境、语句、数据类型、运算、结构)设计:
不能以数字开头(A)、不能用关键字(B)、不能含减号(D)。
7 / 2 的结果是?整数除法直接舍去小数部分,7/2=3。
cin 用
>>;scanf 必须写 &a。int a, b; cin >> a >> b; if (a > b) cout << a; else cout << b;用 if 分支比较即可。
int n, sum = 0; cin >> n; for (int i = 1; i <= n; i++) sum += i; cout << sum;累加循环,别忘了 sum 初始为 0。
二级 · 基础拓展
2.1 计算机存储与网络第二级知识
① 存储器:RAM / ROM / Cache
| 存储器 | 特点 | 用途 | ||||||
|---|---|---|---|---|---|---|---|---|
| R | A | M | 内 | 存 | ||||
| 断 | 电 | 丢 | 失 | 、 | 读 | 写 | 快 | |
| 运 | 行 | 中 | 的 | 程 | 序 | 和 | 数 | 据 |
② 计算机网络
- 分类:局域网 LAN(教室)、城域网 MAN(城市)、广域网 WAN(互联网);
- TCP/IP 四层:应用层、传输层、网络层、网络接口层;OSI 七层:物理层…应用层;
- IP 地址:设备在网络的"门牌号"(如 192.168.1.1)。
2.2 程序设计语言与流程图第二级知识
① 语言分类
| 语言 | 特点 | 例子 | |||||||
|---|---|---|---|---|---|---|---|---|---|
| 机 | 器 | 语 | 言 | ||||||
| 二 | 进 | 制 | , | 机 | 器 | 直 | 接 | 执 | 行 |
| 0 | 1 | 0 | 1 | 1 | 0 | 1 | 0 |
② 流程图
圆角矩形=开始/结束,平行四边形=输入输出,矩形=处理,菱形=判断,箭头=流程方向。
2.3 ASCII 编码第二级知识
① 关键 ASCII 码
| 字符 | ASCII |
|---|---|
| 空 | 格 |
| 3 | 2 |
② 字符与编码转换
char c = 'A';
cout << (int)c << endl; // 65(字符转编码)
cout << (char)97 << endl; // 'a'(编码转字符)
// 小写 = 大写 + 32
cout << (char)('A' + 32) << endl; // 得到字符 a +32;③ (int)c 取编码。2.4 数据类型转换第二级知识
① 强制转换 vs 隐式转换
int a = 5, b = 2;
double d = (double)a / b; // 强制转换:2.5
cout << d << endl;
// 隐式转换:int 参与小树运算自动变 double
cout << 5 / 2.0 << endl; // 2.5
cout << (char)65 << endl; // 得到字符 A (类型)值。2.5 多层分支与多层循环第二级知识
① 分支嵌套
int score = 85;
if (score >= 60) {
if (score >= 90) cout << "优秀";
else cout << "合格";
} else cout << "不合格";② 循环嵌套
for (int i = 1; i <= 5; i++) {
for (int j = 1; j <= i; j++)
cout << "*";
cout << endl;
}2.6 数学函数第二级知识
① 常用数学函数
#include <cmath>
#include <cstdlib>
cout << abs(-5) << endl; // 5 绝对值
cout << sqrt(25) << endl; // 5 平方根
cout << max(3, 7) << " " << min(3, 7) << endl; // 7 3
cout << pow(2, 10) << endl; // 1024 乘方
// 随机数:先播种再取
srand(time(0));
int x = rand() % 6 + 1; // 1~6 随机
cout << x;sqrt 需 #include <cmath>;② rand()%n 得到 0~n-1;③ pow 返回 double。二级模拟题第二级知识
RAM(内存)断电丢失。
A=65、a=97、0=48、空格=32。
5 / 2.0 的结果是?一边是 double,发生隐式转换,得到 2.5。
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++)
cout << j << "*" << i << "=" << i*j << "\t";
cout << endl;
}外层乘数 i,内层从 1 到 i。三级 · 数据与算法起步
3.1 数据编码:原码、反码、补码第三级知识
① 三种编码
| 编码 | 正数 | 负数 | 说明 | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 原 | 码 | ||||||||||||
| 本 | 身 | ||||||||||||
| 符 | 号 | 位 | 1 | + | 绝 | 对 | 值 | ||||||
| 最 | 左 | 边 | 符 | 号 | 位 | : | 0 | 正 | 1 | 负 |
例:8 位表示 -3:原码 10000011,反码 11111100,补码 11111101。
~x = -x-1(按位取反);③ 正数三种编码相同。3.2 进制转换第三级知识
① 常见进制
| 进制 | 基数 | 数字 | 例子 | ||||
|---|---|---|---|---|---|---|---|
| 二 | 进 | 制 | |||||
| 2 | |||||||
| 0 | , | 1 | |||||
| 1 | 0 | 1 | 0 | ₂ | = | 1 | 0 |
② 十进制转二进制(除 2 取余)
int n = 13, a[100], cnt = 0;
while (n) { a[cnt++] = n % 2; n /= 2; }
for (int i = cnt - 1; i >= 0; i--) cout << a[i]; // 1101
// 流操作输出
cout << hex << 255 << endl; // ff把二进制想成4 盏灯(亮=1 灭=0),分量 8、4、2、1:表示 13 就亮 8+4+1 三盏 → 1101。电脑里所有数据都是灯的亮灭!
3.3 位运算第三级知识
① 位运算符
| 符号 | 含义 | 例子 | 结果 | |
|---|---|---|---|---|
| & | ||||
| 按 | 位 | 与 | ||
| 5 | & | 3 | ||
| 1 |
int n = 7;
cout << (n & 1) << endl; // 1 → 奇数(判奇偶)
cout << (n << 1) << endl; // 14(×2)
cout << (n >> 1) << endl; // 3(÷2)
int a = 5, b = 3;
a ^= b; b ^= a; a ^= b; // 交换 a、bx & 1 判奇偶;② 左移=×2、右移=÷2;③ 位运算优先级低,加括号 (n & 1)。3.4 算法的概念与描述第三级知识
① 什么是算法
算法 = 解决某问题的明确步骤。三种描述方式:
- 自然语言:日常话描述步骤;
- 流程图:图形化(菱形判断、矩形处理);
- 伪代码:像代码但不要求能运行。
3.5 一维数组第三级知识
① 数组的定义与使用
int a[100]; // 定义能存 100 个整数的数组
int n; cin >> n;
for (int i = 0; i < n; i++) cin >> a[i];
// 下标从 0 开始
int sum = 0, mx = a[0];
for (int i = 0; i < n; i++) {
sum += a[i];
if (a[i] > mx) mx = a[i];
}
cout << sum << " " << mx << endl;把数组想成一排编号从 0 开始的储物柜:a[0] 是 0 号柜、a[1] 是 1 号柜……循环下标 i 就是挨个开柜子。柜子容量开多大很重要,开小了会"爆柜"(越界)。
3.6 字符串及其函数第三级知识
① 字符串常用函数
#include <string>
string s = "Hello World";
cout << s.size() << endl; // 11 长度
cout << s.substr(0, 5) << endl; // "Hello" 截取
cout << s.find("World") << endl; // 6 查找位置
s += "!"; // 拼接
// 大小写转换
for (char &c : s) if (c >= 'A' && c <= 'Z') c += 32;
cout << s << endl; // 全小写| 函数 | 作用 | ||||||
|---|---|---|---|---|---|---|---|
| s | . | s | i | z | e | ( | ) |
| 长 | 度 |
string::npos;③ 单个字符是 char,用单引号。3.7 枚举法与模拟法第三级知识
① 枚举法
// 水仙花数:各位立方和等于本身
for (int n = 100; n <= 999; n++) {
int a = n / 100, b = n / 10 % 10, c = n % 10;
if (a*a*a + b*b*b + c*c*c == n)
cout << n << " "; // 153 370 371 407
}② 模拟法
// 模拟:猴子吃桃,第 10 天剩 1 个,每天吃一半多一个
int x = 1;
for (int d = 9; d >= 1; d--) x = (x + 1) * 2;
cout << x << endl; // 1534枚举=钥匙丢了挨个抽屉翻,不重不漏;模拟=照着说明书一步步做,题目怎么说代码就怎么写。一个是"穷举验证",一个是"原样还原过程"。
三级模拟题第三级知识
原码 10000101 → 反码 11111010 → 补码 = 反码+1 = 11111011。
13 = 8+4+1 → 1101。
8 >> 2 的值是?右移一位÷2,右移两位÷4:8÷4=2。
int n; cin >> n; int a[100]; for (int i=0;i<n;i++) cin >> a[i]; int mx = a[0]; for (int i=1;i<n;i++) if (a[i]>mx) mx=a[i]; cout << mx;遍历数组找最大。
for (int i=1;i<=100;i++)
if (i%3==0 && i%5==0) cout << i << " ";i%3==0 && i%5==0 即能被 15 整除。四级 · 函数与算法进阶
4.1 指针类型第四级知识
① 指针的概念
指针存的是变量的地址(门牌号)。定义、赋值、解引用:
int x = 10;
int *p = &x; // p 存 x 的地址(&取地址)
cout << *p << endl; // 10(*p 解引用取值)
*p = 20; // 通过指针改 x
cout << x << endl; // 20把指针想成写着门牌号的纸条:变量 x 在地址 &x 的房子,p 拿的是纸条,*p 就是顺着纸条找到房子,开门看里面的值。指针能让你"绕过去"修改别的变量!
&x 取地址、*p 解引用;② 指针必须先赋值再用,否则是野指针;③ 指针类型要和指向的变量一致。4.2 函数:定义、调用、形参实参第四级知识
① 函数的定义与调用
int add(int a, int b) { // a、b 形参
return a + b;
}
int main() {
int r = add(3, 5); // 3、5 实参
cout << r << endl; // 8
return 0;
}② 函数声明 / 定义 / 调用
- 声明:告诉编译器函数长什么样(可放 main 前);
- 定义:写函数体;
- 形参=菜谱里的"适量",实参=实际放的量。
return;② return 后语句不执行;③ 函数一般写在 main 之前或先声明。4.3 作用域与参数传递第四级知识
① 全局与局部
int g = 100; // 全局变量(整个文件可见)
void f() { cout << g; } // 函数里能用全局
int main() {
int g = 5; // 局部变量(遮蔽全局)
cout << g << endl; // 5
return 0;
}② 值传递 / 引用传递 / 指针传递
void by_val(int x) { x = 99; } // 改不了外面
void by_ref(int &x) { x = 99; } // 能改
void by_ptr(int *x) { *x = 99; } // 也能改
int a = 1;
by_val(a); cout << a << endl; // 1
by_ref(a); cout << a << endl; // 99
by_ptr(&a); cout << a << endl; // 99值传递=给对方复印件,改复印件原件没事;引用/指针=给对方原件地址,对方一改原件就变。想通过函数改外面的变量,就用引用 & 或指针 *。
& 和指针 * 能修改实参;③ 数组传参会"退化"成指针。4.4 结构体第四级知识
① 结构体的定义与使用
struct Student {
string name;
int score;
};
Student a[100]; // 结构体数组
cin >> a[0].name >> a[0].score;
cout << a[0].name << " " << a[0].score;
// 结构体排序
bool cmp(Student x, Student y) {
return x.score > y.score; // 按分数从高到低
}
sort(a, a + n, cmp);把结构体想成一个学生档案袋:里面装着名字、分数、班级(不同数据合在一起)。struct 就是先规定档案袋里装什么,然后每个学生一个袋子。
. 访问成员;② 结构体可以放进数组、做函数参数;③ 排序时自定义 cmp 规则。4.5 二维数组与多维数组第四级知识
① 二维数组
int a[10][10]; // 行 × 列
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
cin >> a[i][j];
// 求和
int sum = 0;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
sum += a[i][j];
cout << sum;把二维数组想成电影院座位表:a[i][j] 就是第 i 排第 j 座,先定排(i)再定座(j)。双层循环 = 一排排挨个查座位。
a[行][列];② 双层循环先外层行、内层列;③ 行和列别搞反。4.6 递推算法第四级知识
① 递推
// 斐波那契
long long f[100];
f[1] = f[2] = 1; // 初始值
for (int i = 3; i <= n; i++)
f[i] = f[i - 1] + f[i - 2]; // 递推关系
cout << f[n] << endl;到第 n 级只能从 n-1 走 1 步或 n-2 走 2 步 → f(n)=f(n-1)+f(n-2)。先算小的,再推大的,就是递推。比递归快,因为不重复算。
4.7 排序算法与稳定性第四级知识
① 冒泡 / 插入 / 选择
// 冒泡:相邻比较,大的往后
void bubble(int a[], int n) {
for (int i = 0; i < n - 1; i++)
for (int j = 0; j < n - 1 - i; j++)
if (a[j] > a[j+1]) swap(a[j], a[j+1]);
}
// 选择:每轮挑最小放前面(不稳定)
void select(int a[], int n) {
for (int i = 0; i < n - 1; i++) {
int mn = i;
for (int j = i + 1; j < n; j++)
if (a[j] < a[mn]) mn = j;
swap(a[i], a[mn]);
}
}② 稳定性与复杂度
稳定性:相等元素排序后相对顺序不变。冒泡、插入稳定;选择不稳定。三种都是 O(n²)。
冒泡=相邻比个子高往后挪;选择=每轮挑最矮的站前面;插入=打扑克抓牌插入合适位置。排序稳定性就像同分数的学生按原来先后顺序排,不乱插队。
sort 更快(O(n log n))。4.8 算法复杂度估算第四级知识
① 时间复杂度
| 结构 | 复杂度 | 例子 | ||
|---|---|---|---|---|
| 普 | 通 | 语 | 句 | |
| O | ( | 1 | ) | |
| a | = | 1 |
4.9 文件读写与异常处理第四级知识
① 文件重定向
freopen("in.txt", "r", stdin); // 从文件读
freopen("out.txt", "w", stdout); // 写到文件
int a; cin >> a; cout << a; // 正常写代码
fclose(stdin); fclose(stdout);② 文件读写流
#include <fstream>
ifstream fin("in.txt");
ofstream fout("out.txt");
int x; fin >> x; // 读
fout << x * 2; // 写
fin.close(); fout.close();freopen 重定向后 cin/cout 自动转向文件;② 读不到数据时 fin 状态变 false;③ GESP 机试常用 freopen 读文件。四级模拟题第四级知识
引用(或指针)传递能修改实参;值传递不行。
选择排序可能交换相等元素,不稳定。
解引用 *p 得到指针指向变量的值。
struct S{ string name; int score; } a[100];
bool cmp(S x, S y){ return x.score > y.score; }
sort(a, a+n, cmp);自定义 cmp 按分数降序。freopen("in.txt","r",stdin);
int n, sum=0, x; cin >> n;
for (int i=0;i<n;i++){ cin >> x; sum+=x; }
cout << sum;重定向后 cin 从文件读取。五级 · 数论与高效算法
5.1 初等数论第五级知识
① 素数判断
bool is_prime(int n) {
if (n < 2) return false;
for (int i = 2; i * i <= n; i++)
if (n % i == 0) return false;
return true;
}② 最大公约数与最小公倍数
int gcd(int a, int b) { // 辗转相除法
while (b) { int t = a % b; a = b; b = t; }
return a;
}
int lcm(int a, int b) { return a / gcd(a, b) * b; }③ 素数与质因数分解
// 质因数分解 12 = 2*2*3
int n = 12;
for (int i = 2; i * i <= n; i++)
while (n % i == 0) { cout << i << " "; n /= i; }
if (n > 1) cout << n; // 最后剩下的质数
// 输出 2 2 35.2 高精度运算(数组模拟)第五级知识
① 高精度加法
// 数字倒着存数组:a[0] 是个位
int a[500] = {0}, b[500] = {0}, c[505] = {0};
string s1 = "12345678901234567890", s2 = "9876543210";
for (int i = 0; i < s1.size(); i++) a[i] = s1[s1.size()-1-i] - '0';
for (int i = 0; i < s2.size(); i++) b[i] = s2[s2.size()-1-i] - '0';
int len = max(s1.size(), s2.size());
for (int i = 0; i < len; i++) {
c[i] += a[i] + b[i];
c[i+1] += c[i] / 10; // 进位
c[i] %= 10;
}
if (c[len]) len++;
for (int i = len - 1; i >= 0; i--) cout << c[i];数字太大 long long 也装不下(超过 19 位)。高精度就是小学生列竖式:个位对齐、逐位相加、满十进一。数组倒着存(个位在 a[0])是为了进位方便!
c[i+1] += c[i]/10; c[i] %= 10;;③ 减法注意借位、乘法注意错位相加。5.3 链表第五级知识
① 单链表的创建与操作
struct Node { int data; Node *next; };
// 头插法建链表
Node *head = nullptr;
for (int i = 1; i <= 5; i++) {
Node *p = new Node();
p->data = i;
p->next = head; // 新结点指向原头
head = p;
}
// 遍历
for (Node *p = head; p; p = p->next)
cout << p->data << " ";② 单 / 双 / 循环链表
- 单链表:只能往后走(单向);
- 双链表:有 prev 和 next,能双向走,删除更灵活;
- 循环链表:尾结点指向头,形成环(约瑟夫问题常用)。
链表=小朋友手拉手排成一队,每个人只记着"下一个是谁"(next 指针)。插入就像中间塞进一个小朋友:先让他拉住前面的,再让前面的人改拉他。不用整队重排!
new 建结点、-> 访问成员;② 删除结点要先改指针再释放;③ 链表插入/删除 O(1),查找 O(n)。5.4 埃氏筛与线性筛第五级知识
① 埃氏筛
const int N = 1000000;
bool isp[N + 1];
void sieve(int n) {
fill(isp, isp + n + 1, true);
isp[0] = isp[1] = false;
for (int i = 2; i * i <= n; i++)
if (isp[i])
for (int j = i * i; j <= n; j += i) // 从 i*i 开始筛
isp[j] = false;
}② 线性筛(欧拉筛,O(n))
vector<int> primes;
bool np[N + 1];
for (int i = 2; i <= n; i++) {
if (!np[i]) primes.push_back(i);
for (int j = 0; j < primes.size() && i * primes[j] <= n; j++) {
np[i * primes[j]] = true;
if (i % primes[j] == 0) break; // 保证每个数只被最小质因子筛一次
}
}5.5 二分查找与二分答案第五级知识
① 二分查找
int a[100], n, x;
// 前提:a 已升序
int l = 0, r = n - 1, ans = -1;
while (l <= r) {
int mid = (l + r) / 2;
if (a[mid] == x) { ans = mid; break; }
else if (a[mid] < x) l = mid + 1;
else r = mid - 1;
}
cout << ans;② 二分答案
// 例:m 段绳子切成长度 len,每段最长多少
bool ok(int len, int a[], int m) {
long long cnt = 0;
for (int i = 0; i < n; i++) cnt += a[i] / len;
return cnt >= m;
}
int l = 1, r = maxlen;
while (l <= r) {
int mid = (l + r) / 2;
if (ok(mid, a, m)) l = mid + 1; // 还能更长
else r = mid - 1;
}
cout << r;猜字在字典哪页:翻中间,大了往左翻、小了往右翻,每次砍一半——二分查找。二分答案=题目反过来问"最长能多长":猜一个长度,验证够不够,再收窄。100 万个数最多 20 次!
l=mid+1 / r=mid-1 防死循环;③ 验证函数 ok() 要写对。5.6 递归算法第五级知识
① 递归
int fact(int n) {
if (n <= 1) return 1; // 终止条件
return n * fact(n - 1); // 自己调自己
}
int fib(int n) {
if (n <= 2) return 1;
return fib(n - 1) + fib(n - 2); // O(2^n) 太慢!
}② 优化:记忆化
long long memo[100];
long long fib2(int n) {
if (n <= 2) return 1;
if (memo[n]) return memo[n]; // 算过直接返回
return memo[n] = fib2(n - 1) + fib2(n - 2);
}递归像俄罗斯套娃:拆大娃得小娃,拆到最小的(终止条件),再一层层带回答案。记忆化像每拆一个娃都记下里面装了多少——下次遇到直接抄答案,不重复拆!
5.7 分治算法:归并与快排第五级知识
① 归并排序
void merge_sort(int a[], int l, int r) {
if (l >= r) return;
int mid = (l + r) / 2;
merge_sort(a, l, mid);
merge_sort(a, mid + 1, r);
// 合并两个有序段
int tmp[100], i = l, j = mid + 1, k = l;
while (i <= mid && j <= r)
tmp[k++] = a[i] < a[j] ? a[i++] : a[j++];
while (i <= mid) tmp[k++] = a[i++];
while (j <= r) tmp[k++] = a[j++];
for (i = l; i <= r; i++) a[i] = tmp[i];
}② 快速排序(选基准分两边)
void quick_sort(int a[], int l, int r) {
if (l >= r) return;
int i = l, j = r, p = a[l];
while (i < j) {
while (i < j && a[j] >= p) j--;
a[i] = a[j];
while (i < j && a[i] <= p) i++;
a[j] = a[i];
}
a[i] = p;
quick_sort(a, l, i - 1);
quick_sort(a, i + 1, r);
}5.8 贪心算法第五级知识
① 贪心
// 活动安排:按结束时间排序,每次选最早结束且不冲突的
struct Act { int s, e; };
bool cmp(Act a, Act b) { return a.e < b.e; }
sort(act, act + n, cmp);
int cnt = 0, last = -1;
for (int i = 0; i < n; i++)
if (act[i].s >= last) { cnt++; last = act[i].e; }
cout << cnt;贪心=每次都挑当前看起来最好的:活动安排就选"结束最早的",腾出最多时间。但要小心:不是所有问题"每次最优"都对,要先证明局部最优能推出全局最优。
五级模拟题第五级知识
48%36=12,36%12=0 → gcd=12。
每次砍半,约 log₂n+1 次。
埃氏筛 O(n log log n),线性筛 O(n)。
// 倒序存数组,逐位相加,满十进一
int c[505]={0};
for (int i=0;i<len;i++){ c[i]+=a[i]+b[i]; c[i+1]+=c[i]/10; c[i]%=10; }个位对齐、逐位加、处理进位。bool ok = (n >= 2);
for (int i=2; i*i<=n; i++)
if (n % i == 0) { ok = false; break; }试到 √n 即可。六级 · 数据结构与搜索
6.1 树的基本概念与遍历第六级知识
① 树的定义
- 树:n 个结点的分层结构,唯一根,每个结点可有多个孩子;
- 叶子=没有孩子的结点,深度=层数;
- 二叉树的每个结点最多 2 个孩子(左、右)。
② 二叉树遍历
struct Node { int v; Node *l, *r; };
void preorder(Node *p) { // 前序:根-左-右
if (!p) return;
cout << p->v << " "; preorder(p->l); preorder(p->r);
}
void inorder(Node *p) { // 中序:左-根-右
if (!p) return;
inorder(p->l); cout << p->v << " "; inorder(p->r);
}
void postorder(Node *p) { // 后序:左-右-根
if (!p) return;
postorder(p->l); postorder(p->r); cout << p->v << " ";
}6.2 哈夫曼树与哈夫曼编码第六级知识
① 哈夫曼树的构造
例:权值 {2,3,4,7}:先合 2+3=5 → {4,5,7},再合 4+5=9 → {7,9},再合 7+9=16。带权路径长度最小。
② 哈夫曼编码
左 0 右 1,频率高的字符路径短(编码短),是前缀编码(任何编码不是另一个的前缀),可无歧义解码。
6.3 完全二叉树与二叉排序树第六级知识
① 完全二叉树
除最后一层外全满,最后一层靠左。数组存储:i 的左孩子 2i、右孩子 2i+1、父亲 i/2。
② 二叉排序树 BST
Node* insert(Node *p, int x) {
if (!p) { p = new Node(); p->v = x; return p; }
if (x < p->v) p->l = insert(p->l, x);
else p->r = insert(p->r, x);
return p;
}
// BST 中序遍历 = 有序输出6.4 哈夫曼编码与格雷编码第六级知识
① 格雷编码
格雷码:相邻两个数的二进制只有一位不同。用途:旋转编码器、信号防错。
// 十进制 n 转格雷码:g = n ^ (n >> 1)
for (int i = 0; i < 8; i++) {
int g = i ^ (i >> 1);
cout << i << "->" << g << endl;
}
// 0->0 1->1 2->3 3->2 4->6 5->7 6->5 7->4g = n ^ (n>>1);③ 与二进制是"逐位"差异。6.5 深度优先搜索 DFS第六级知识
① DFS
bool vis[1005];
vector<int> g[1005];
void dfs(int u) {
vis[u] = true;
cout << u << " ";
for (int v : g[u])
if (!vis[v]) dfs(v);
}DFS=迷宫探险家一条道走到黑,撞墙回头(回溯)换一条,走过的路做记号(vis)防绕圈。用递归实现最自然。
vis 标记;③ 适合求所有方案、连通块、全排列。6.6 广度优先搜索 BFS第六级知识
① BFS
#include <queue>
queue<int> q;
bool vis[1005];
vector<int> g[1005];
void bfs(int s) {
q.push(s); vis[s] = true;
while (!q.empty()) {
int u = q.front(); q.pop();
cout << u << " ";
for (int v : g[u])
if (!vis[v]) { vis[v] = true; q.push(v); }
}
}BFS=往池塘扔石子,水波一圈圈外扩:先到的地方离起点最近。用队列实现(先来先出)。找最短步数最合适!
6.7 简单动态规划第六级知识
① 一维 DP:爬楼梯
long long dp[100];
dp[0] = 1;
for (int i = 1; i <= n; i++) {
dp[i] = dp[i - 1];
if (i >= 2) dp[i] += dp[i - 2];
}
cout << dp[n];② 0-1 背包
int W, n, w[105], v[105], dp[10005];
cin >> W >> n;
for (int i = 1; i <= n; i++) cin >> w[i] >> v[i];
for (int i = 1; i <= n; i++)
for (int j = W; j >= w[i]; j--) // 倒序!
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
cout << dp[W];0-1 背包=10kg 的箱子,每件物品只能带一个,怎么装最值钱?dp[j]=容量 j 的最大价值。倒序循环保证每件物品只装一次!
6.8 面向对象:类第六级知识
① 类与对象
class Dog {
public:
Dog(string n, int a) { name = n; age = a; } // 构造函数
void bark() { cout << name << " 汪汪!" << endl; }
private:
string name; // 封装:私有成员
int age;
};
int main() {
Dog d("旺财", 3);
d.bark(); // 旺财 汪汪!
return 0;
}② 三大特性
- 封装:private 隐藏数据,通过 public 方法访问;
- 继承:子类继承父类属性和方法;
- 多态:同一接口不同实现(虚函数 virtual)。
6.9 栈、队列、循环队列第六级知识
① 栈(后进先出)
#include <stack>
stack<int> st;
st.push(1); st.push(2);
cout << st.top(); // 2(栈顶)
st.pop(); // 弹出② 队列(先进先出)
#include <queue>
queue<int> q;
q.push(1); q.push(2);
cout << q.front(); // 1
q.pop(); // 出队循环队列:数组实现时队尾回绕到队首,避免假溢出。可用 STL deque 双端队列。
栈=食堂叠盘子后放先拿(后进先出);队列=排队打饭先来先走(先进先出)。栈常用于括号匹配、函数调用,队列常用于 BFS、排队模拟。
六级模拟题第六级知识
中序=左根右。
倒序保证每件物品只取一次。
BFS 用队列(先进先出)。
格雷码相邻两个数二进制只有 1 位不同。
stack<char> st;
for (char c : s) {
if (c=='(' || c=='[') st.push(c);
else {
if (st.empty()) { 不匹配; break; }
char t = st.top(); st.pop();
if (不配对) { 不匹配; break; }
}
}
// 最后 st 空则匹配左括号入栈,右括号与栈顶配对。queue 存 (x,y,step)
while (q非空) {
取队首; 若到终点输出 step;
四个方向扩展,可走且未访问则入队并记步数
}第一次到达终点的步数即最短。七级 · 动态规划与图论
7.1 数学库常用函数第七级知识
① 三角 / 对数 / 指数
#include <cmath>
sin(M_PI / 2) // 1 正弦(弧度)
cos(0) // 1 余弦
log10(1000) // 3 以 10 为底对数
log2(8) // 3 以 2 为底对数
exp(1) // e ≈ 2.718
pow(2, 10) // 10247.2 复杂动态规划第七级知识
① 最长上升子序列 LIS
int a[1005], dp[1005];
for (int i = 0; i < n; i++) {
dp[i] = 1;
for (int j = 0; j < i; j++)
if (a[j] < a[i]) dp[i] = max(dp[i], dp[j] + 1);
}
int ans = 0;
for (int i = 0; i < n; i++) ans = max(ans, dp[i]);
cout << ans; // O(n^2)② 最长公共子序列 LCS
string s1 = "abcde", s2 = "ace";
int dp[1005][1005];
for (int i = 1; i <= s1.size(); i++)
for (int j = 1; j <= s2.size(); j++) {
if (s1[i-1] == s2[j-1]) dp[i][j] = dp[i-1][j-1] + 1;
else dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
}
cout << dp[s1.size()][s2.size()]; // 3③ 区间 DP 与滚动数组
- 区间 DP:状态是"区间 [i,j]",如石子合并:
dp[i][j] = min(dp[i][k]+dp[k+1][j]) + cost; - 滚动数组:只保留上一行,把空间从 O(n²) 降到 O(n)。
7.3 图的定义与遍历第七级知识
① 图的定义
- 图 = 顶点 + 边;有向图(单向)、无向图(双向);
- 顶点的度:无向图=相连边数,有向图分入度/出度;
- 存储方式:邻接矩阵(二维数组)、邻接表(vector 套娃)。
② 存储与遍历
vector<int> g[1005]; // 邻接表
g[u].push_back(v); // 加边 u→v
// DFS / BFS 遍历与树相同,加 vis 防重复7.4 泛洪算法 flood fill第七级知识
① 连通块计数
int a[105][105], n, m;
int dx[4] = {1, -1, 0, 0}, dy[4] = {0, 0, 1, -1};
void flood(int x, int y) {
a[x][y] = 0; // 淹掉(标记访问)
for (int k = 0; k < 4; k++) {
int nx = x + dx[k], ny = y + dy[k];
if (nx >= 0 && nx < n && ny >= 0 && ny < m && a[nx][ny] == 1)
flood(nx, ny);
}
}
int cnt = 0;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
if (a[i][j] == 1) { cnt++; flood(i, j); }
cout << cnt;flood fill=看小岛地图,从一块陆地出发把相连整片染色(访问=淹掉),数出共有几座岛。DFS/BFS 都行,关键是整片一起处理、走过的标记掉。
7.5 哈希表第七级知识
① 哈希思想
用哈希函数把键映射到数组下标,实现平均 O(1) 的插入/查找。冲突用链地址法或开放寻址法解决。
#include <unordered_map>
#include <unordered_set>
unordered_map<string, int> mp;
mp["apple"] = 3; // 插入
cout << mp["apple"]; // 3 O(1)
unordered_set<int> st;
st.insert(5);
cout << st.count(5); // 1 判断存在哈希表=按首字母分格的储物架:找 "apple" 直接去 A 格,不用一格格翻。哈希函数帮你算好位置,冲突=两个词撞进同格,用"链子"串起来。C++ 的 unordered_map 底层就是哈希表。
七级模拟题第七级知识
2⁴=16 → log2(16)=4。
经典定义:以 a[i] 结尾的 LIS 长度。
有向图:出边数=出度,入边数=入度。
unordered_map<char,int> mp; for (char c : s) mp[c]++;遍历字符串逐字符计数。
八级 · 综合与优化
8.1 计数原理:加法与乘法第八级知识
① 加法原理 / 乘法原理
- 加法原理(分类,互斥):做一件事有 m 种"或"n 种方法 → m+n;
- 乘法原理(分步,连续):分两步各有 m、n 种 → m×n。
套餐 A 或 B 任选=加法(2 种);主食+饮料各选一个=乘法(3×2=6 种)。加"或"、乘"和"——分类相加、分步相乘!
8.2 排列与组合第八级知识
① 排列 P(n,k)(讲究顺序)
从 n 个不同元素取 k 个排成一列:P(n,k) = n!/(n-k)!。例:3 人排队选 2 人 P(3,2)=6。
② 组合 C(n,k)(不讲究顺序)
取 k 个成一组:C(n,k) = n!/(k!(n-k)!)。例:3 人选 2 人 C(3,2)=3。
// 用递推求组合数 C(n,k)(杨辉三角法)
long long C[50][50];
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]; // 防溢出
}8.3 杨辉三角第八级知识
① 杨辉三角
int t[20][20];
for (int i = 0; i < n; i++) {
t[i][0] = t[i][i] = 1; // 两边是 1
for (int j = 1; j < i; j++)
t[i][j] = t[i-1][j-1] + t[i-1][j]; // 肩上两数相加
}
// 第 i 行第 k 个数 = C(i, k)杨辉三角像人墙:最边上是 1 个人,中间每个人站在下面两个人肩膀上(肩上两数相加)。每行数字正好是组合数!
8.4 倍增法第八级知识
① 倍增的思想
倍增:每次按 2 的幂(1,2,4,8…)跳,把 O(n) 优化到 O(log n)。应用:快速幂、倍增求 LCA、ST 表、稀疏表。
// 快速幂:a^b % m
long long qpow(long long a, long long b, long long m) {
long long res = 1;
while (b) {
if (b & 1) res = res * a % m;
a = a * a % m;
b >>= 1;
}
return res;
}
// qpow(2, 10, 1e9+7) = 1024一格一格跳 1000 步太慢。倍增=先大步跳 512、256、128…(二进制拆分:1000=512+256+128+64+32+8),只跳几次就到位!
8.5 代数与平面几何(初中数学)第八级知识
① 一元一次 / 二元一次方程
// 解 ax + b = 0
double a = 2, b = -8;
cout << -b / a << endl; // 4
// 解方程组(克莱默法则):a1x+b1y=c1, a2x+b2y=c2
double a1=1,b1=1,c1=5, a2=2,b2=-1,c2=1;
double det = a1*b2 - a2*b1; // 行列式
double x = (c1*b2 - c2*b1) / det; // 2
double y = (a1*c2 - a2*c1) / det; // 3
cout << x << " " << y;② 平面图形面积
| 图形 | 公式 | |||
|---|---|---|---|---|
| 长 | 方 | 形 | ||
| 长 | × | 宽 |
8.6 最小生成树与最短路第八级知识
① Kruskal(选最小边 + 并查集)
struct Edge { int u, v, w; };
bool cmp(Edge a, Edge b) { return a.w < b.w; }
int fa[1005];
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
sort(edges.begin(), edges.end(), cmp);
long long ans = 0;
for (auto &e : edges) {
int fu = find(e.u), fv = find(e.v);
if (fu != fv) { fa[fu] = fv; ans += e.w; } // 不成环就选
}② Prim(加点法)
从一个点出发,每次选连接已选集合的最短边加入新点,重复 n-1 次。适合稠密图。
③ Dijkstra(单源最短路)
#include <queue>
vector<pair<int,int>> g[1005];
long long dist[1005];
priority_queue<pair<long long,int>, vector<pair<long long,int>>, greater<>> pq;
dist[s] = 0; pq.push({0, s});
while (!pq.empty()) {
auto [d, u] = pq.top(); pq.pop();
if (d > dist[u]) continue;
for (auto [v, w] : g[u])
if (dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
pq.push({dist[v], v});
}
}④ Floyd(全源最短路,O(n³))
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if (d[i][k] + d[k][j] < d[i][j])
d[i][j] = d[i][k] + d[k][j];8.7 算法时空效率分析与优化第八级知识
① 时空效率分析
- 时间:大 O 表示增长趋势;空间:额外内存;
- 常见:排序 O(n²)/O(n log n)、查找 O(n)/O(log n)、遍历 O(n)、DP 看状态×转移。
② 算法优化
- 数学优化:等差数列求和公式 n(n+1)/2 代替循环;
- 数据结构优化:哈希、前缀和、ST 表、线段树;
- 剪枝:搜索提前排除不可能分支;
- 空间换时间:预处理缓存;
- 二分/倍增:把线性变成对数。
数 1+2+…+100:循环加走 100 步,数学公式 100×101÷2 一步算完——数学优化用公式省掉大量循环!
八级模拟题第八级知识
分步乘法:3×2=6。
C(5,2)=5×4/2=10。
每次指数减半 → O(log b)。
并查集(union-find)快速判环。
// 边按权排序 + 并查集
sort(edges, cmp);
for (边 e) if (find(u)!=find(v)) { 合并; ans+=w; }选最小边、不成环就加。long long qpow(long long a, long long b, long long m){
long long r=1;
while(b){ if(b&1) r=r*a%m; a=a*a%m; b>>=1; }
return r;
}二进制拆分指数,边平方边取模。