第一部分 选择题 1~7
第1题:单链表尾部插入,究竟有多快?
第1题:单链表尾部插入,究竟有多快?
题目:
小杨用单链表保存任务序列,并同时维护头指针
head和尾指针tail。在链表非空且已知tail的情况下,在表尾插入新结点的时间复杂度是( )。
答案:A
一、先想象一个“排队的小火车”
单链表可以想象成一列小火车:
head ↓ [任务1] → [任务2] → [任务3] → nullptr ↑ tail每个结点里面有两个东西:
struct Node { int value; Node *next; };一个存数据:
value一个负责指向下一个结点:
next二、如果没有 tail,会发生什么?
假设我们只有:
head现在想在最后面添加:
[1] → [2] → [3] → [4]我们不知道谁是最后一个。
只能从头开始找:
1 → 2 → 3 → 4 ↑ 从这里开始找如果有n个结点,就可能需要走n
步。
所以:
时间复杂度:O(n)三、可是题目说:我有 tail!
现在:
head ↓ [1] → [2] → [3] ↑ tail要加入:
[4]直接:
tail->next = newNode; tail = newNode;只做两三件固定的事情。
不管链表里面有:
10个 100个 10000个 1000000个都差不多这么几步。
所以:
O(1)⭐ 小学生记忆法
有尾指针 tail,找队伍最后一个人就不用从头找。
已知 tail → 尾部插入 O(1)
第2题:双向链表删除结点
题目说:
在不带哨兵结点的双向链表中,结点
p既不是头结点也不是尾结点。删除p的正确代码是( )。
答案:C 。 单链表: B 只知道: 双向链表则是: B 不但知道 C: 还知道 A: 所以: 原来: 删除 B 后,我们希望变成: 那么要修改哪两个“指针”? 让 A 跳过 B,直接指向 C: 图: 让 C 反过来指向 A: 图: 最后: 所以完整代码: 答案: C 比如: 这只是修改了p 自己的 prev 。 但是我们真正需要修理的是: 也就是说: 删除一个结点,不是只修改“自己”,而是要把前后两个邻居重新牵手。 删除双向链表中的 : 即: 题目给出了: 答案:B: 原题程序结构见试卷。 想象学校运动会上有两位同学: 小慢:一次走1格 小快:一次走2格 例如: 一开始: 每轮: 第一轮: 第二轮: 此时: 刚好来到中间! 因为: 当 fast 走完整条链表时: 所以 slow 就停在中间附近。 slow: 一次走一步。 那么 fast 就必须: 一次走两步。 答案: B 慢指针一步走,快指针两步走。 看到: 脑袋里立刻出现: 题目: 求: 答案:C:15 。 105 和 45 的公约数: 最大的: 所以: 不过考试不会让我们一个一个试。 这里使用的是非常经典的: 欧几里得算法,也叫辗转相除法。 核心: 会变成: 我们一步一步算。 余数: 所以: 余数: 所以: 因为: 于是: 返回: 欧几里得算法就像: 大石头不断用小石头去除,最后剩下的那个“最后的非零余数”,就是最大公约数。 口诀: 题目: 选项: 答案:D 。 质数就是: 大于1,并且只能被1和自己整除的数。 例如: 不是质数: 判断: 是不是质数。 最笨: 全部试。 太慢了! 为什么? 假设: 如果: 那么: 也就是说,如果一个数是合数,那么它一定至少有一个因数: 所以我们只需要检查: D: 它表示: 也就是: 这正是最经典的质数判断范围。 这个细节非常重要! 如果: 是 ,那么: 可能发生整数溢出 。 例如 i 很大的时候: 超过 能表示的范围,就可能出问题。 所以写: 更加安全。 判断质数,不必试到 n,只试到 √n。 代码模板: 看到它,就应该马上想到: 质数判断! 这一题开始稍微有点难度了。 题目给出的核心代码: 选项: 答案:B: 原题线性筛代码见试卷。 我们想找: 这些质数。 而: 都是合数。 线性筛的目标非常牛: 每一个合数,只让它被自己的最小质因子筛掉一次。 例如: 最小质因子是: 最小质因子: 最小质因子: 代码: 说明: 被筛掉。 但是什么时候应该停止? 答案: 质数从小到大: 首先: 筛: 然后: 成立! 说明: 这时候: 停止。 因为如果继续: 以后又可能通过其他方式把: 筛掉。 线性筛就是想做到: 一个合数只认一个“最小质因子”,不重复处理。 所以: 答案: B 线性筛遇到“p 能整除 i”,说明 p 已经是 i 的最小质因子,该停了。 题目: 根据唯一分解定理,整数……的正确质因数分解是( )。 本题在 PDF 的文字解析中,题目的具体被分解整数和选项公式出现了排版丢失,因此仅凭当前提取文本无法可靠还原第7题的具体数字和四个选项 。原卷目录位置明确显示第7题紧接在线性筛之后。 但这道题考察的知识点可以确定是: 唯一分解定理 + 质因数分解 第7题答案是: A 。 一个大于 1 的整数,都可以写成: 若干个质数相乘的形式,而且这种分解方式是唯一的(除了质因子的排列顺序不同)。 例如: 可以分解: 也可以写: 虽然顺序不同,但是本质完全一样: 比如: 不断拆: 所以: 也就是: 这就是质因数分解。 比如: 你无论怎么拆: 最终都会得到: 不会出现另外一套完全不同的质因数。 所以: 大整数就像一座城堡,质数是它最基本的砖块。 城堡可以有不同的拆墙顺序,但是最后使用的“砖块种类和数量”是固定的。 这一组题其实不是七个完全孤立的知识点。 我们把它们串起来: 可以把它看成一场“算法升级冒险” : 学会: 核心思想: 指针就是“告诉你下一个人在哪里”。 学会: 用来寻找链表中点。 欧几里得算法: 不断缩小问题。 不要傻傻检查: 只检查: 普通筛法: 一个合数可能被标记很多次。 线性筛: 一个合数只让最小质因子负责。 最终发现: 所有大于1的整数,都可以拆成唯一的一组质数“积木”。 如果把这 7 道题压缩成7 句话 ,同学们可以这样记: ① 有 tail,尾插 O(1)。 ② 删 p,前后邻居重新牵手。 ③ 慢一快二找中点。 ④ gcd 不断换成(b, a%b)。 ⑤ 质数只查到 √n。 ⑥ 线性筛遇到i%p==0就停。 ⑦ 每个整数都有唯一的质因数积木。一、先认识“双向链表”
A → B → C后面是谁?A ⇄ B ⇄ CB.next = CB.prev = Aprev next ← → [A] ⇄ [B] ⇄ [C]二、现在要删除 B
A ⇄ B ⇄ CA ⇄ C第一步
p->prev->next = p->next;A.next = C第二步
p->next->prev = p->prev;C.prev = Adelete p;p->prev->next = p->next; p->next->prev = p->prev; delete p;⭐ 为什么其他思路容易错?
p->prev = p->next;A.next C.prev🧠 记忆口诀
p前面的 next 跳过 p 后面的 prev 跳过 p 最后 delete pp->prev->next = p->next; p->next->prev = p->prev; delete p;第3题:快慢指针寻找链表中点
Node *middle(Node *head) { Node *slow = head; Node *fast = head; while (fast != nullptr && fast->next != nullptr) { slow = slow->next; ______________________ } return slow; }fast = fast->next->next;一、什么叫“快慢指针”?
1 → 2 → 3 → 4 → 5slow ↓ 1 fast ↓ 1slow走1步 fast走2步二、跑一跑
1 → 2 → 3 → 4 → 5 ↑ slow ↑ fast1 → 2 → 3 → 4 → 5 ↑ slow ↑ fastslow = 3三、为什么这么神奇?
fast速度 = slow速度 × 2fast走了 n slow只走了 n/2四、横线应该写什么?
slow = slow->next;fast = fast->next->next;⭐ 记忆口诀
slow fast🐢 一步 🐇 两步第4题:欧几里得算法求最大公约数
int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }gcd(105,45)一、什么是最大公约数?
1 3 5 1515gcd(105,45)=15二、看看代码到底在干什么
gcd(a,b)gcd(b,a%b)第一次
105 ÷ 4515gcd(105,45) = gcd(45,15)第二次
45 ÷ 150gcd(45,15) = gcd(15,0)第三次
b == 0return a;15⭐ 记忆口诀
gcd(a,b) ↓ gcd(b,a%b) ↓ 直到 b=0 ↓ 答案就是 a第5题:怎么判断一个数是不是质数?
bool isPrime(int n) { if (n < 2) return false; for (int i = 2; __________________; i++) { if (n % i == 0) return false; } return true; }A. i < n B. i <= n / 2 C. i * i < n D. (long long)i * i <= n一、什么是质数?
2、3、5、7、11、13……4、6、8、9、10……二、最笨的方法
972、3、4、5、6、7、……96三、其实只需要检查到 √n
n = a × ba > √nb < √n≤ √ni × i <= n四、为什么选 D?
(long long)i * i <= ni² ≤ ni ≤ √n五、为什么还要
(long long)?
iinti * ii × iint(long long)i * i⭐ 记忆口诀
for (int i = 2; (long long)i * i <= n; i++)第6题:线性筛为什么能做到“一合数只筛一次”?
for (int p : primes) { if ((long long)i * p > n) break; composite[i * p] = true; if (__________________) break; }A. p % i == 0 B. i % p == 0 C. i == p D. i * p == ni % p == 0一、先理解“筛子”
2、3、5、7、11……4、6、8、9、10……二、什么叫最小质因子?
12 = 2 × 2 × 3215 = 3 × 5335 = 5 × 75三、关键问题来了
composite[i * p] = true;i × pif (i % p == 0) break;四、举个例子:i = 12
12 = 2 × 2 × 32、3、5、7……p = 212 × 2 = 2412 % 2 == 02 是 12 的最小质因子break;五、为什么必须停止?
12 × 3 = 3636if (i % p == 0) break;🧠 一句话记住
第7题:唯一分解定理——质因数分解
一、什么叫“唯一分解定理”?
1212 = 2 × 2 × 312 = 3 × 2 × 22² × 3二、再看一个例子
6060 ↓ ÷2 30 ↓ ÷2 15 ↓ ÷3 5 ↓ ÷5 160 = 2 × 2 × 3 × 560 = 2² × 3 × 5三、为什么叫“唯一”?
6060 = 2 × 30 = 2 × 2 × 15 = 2 × 2 × 3 × 52、2、3、5🌟 1~7题串起来看
第1题 单链表 ↓ 第2题 双向链表 ↓ 第3题 快慢指针 ↓ 第4题 最大公约数 ↓ 第5题 质数判断 ↓ 第6题 线性筛 ↓ 第7题 质因数分解🏰 第一关:链表村
head tail next prev🐢 第二关:快慢赛跑
slow:一步 fast:两步🧙 第三关:数学魔法
gcd(a,b) → gcd(b,a%b)🔍 第四关:质数侦探
2~n-12~√n⚡ 第五关:线性筛
🧱 第六关:质因数分解
📌 考试最后一分钟速记表
题目 核心知识 记忆口诀 1 单链表尾插 有 tail,尾插O(1)2 双向链表删除 前 next跳过,后prev跳过3 快慢指针 slow一步,fast两步4 gcd gcd(a,b)=gcd(b,a%b)5 质数判断 只检查到√n 6 线性筛 i%p==0就停止7 唯一分解 每个数都有唯一的质因数“积木”