第8题:递归函数f(4)到底是多少?
题目:
int f(int n) { if (n == 1) return 1; return n + f(n - 1); }问:
f(4)是多少?
答案:D:10。
① 先别害怕“递归”
很多同学看到:
f(n)又出现:
f(n - 1)会觉得:
“函数怎么又调用自己了?不会无限循环吗?”
其实不会。
因为这里有一个非常重要的“刹车”:
if (n == 1) return 1;这就是:
🛑递归的终点,也叫递归出口。
② 我们把f(4)拆开
程序:
return n + f(n - 1);所以:
f(4) = 4 + f(3)继续:
f(3) = 3 + f(2)继续:
f(2) = 2 + f(1)到了:
f(1) = 1因为触发:
if (n == 1) return 1;③ 从最里面往外算
现在开始“返回”:
f(1) = 1 f(2) = 2 + 1 = 3 f(3) = 3 + 3 = 6 f(4) = 4 + 6 = 10所以:
答案 = 10
🌟 把它想成爬楼梯
f(4)就像问:
“从第4层往下走,每一层把自己的数字加起来,最后走到1层。”
4 ↓ 3 ↓ 2 ↓ 1最后:
4 + 3 + 2 + 1 = 10🧠 记忆口诀
递归题:先找到出口,再一层一层拆,最后倒着算回来。
第9题:二分查找“第一个大于 x”的位置
这道题非常重要。
题目:
int upperBound(const vector &a, int x) { int l = 0, r = (int)a.size(); while (l < r) { int mid = l + (r - l) / 2; if (__________________) { l = mid + 1; } else { r = mid; } } return l; }题目要求:
在升序数组中,找第一个严格大于
x的元素位置。
答案:C:
a[mid] <= x① 什么叫“第一个严格大于”?
比如:
数组: 1 3 3 5 7 9如果:
x = 3我们寻找:
第一个大于3的数。
答案是:
5它的位置:
0 1 2 3 4 5 ↑所以答案位置是:
3② 为什么判断a[mid] <= x?
想象我们站在mid这个位置。
情况一:
如果:
a[mid] <= x例如:
a[mid] = 3 x = 3我们要找的是:
严格大于 3
所以3肯定不行。
怎么办?
往右找!于是:
l = mid + 1;情况二:
如果:
a[mid] > x例如:
a[mid] = 7 x = 37 已经符合要求。
但是:
会不会左边还有一个更小的位置,也同样大于3?
当然可能!
所以不能直接结束。
应该:
往左缩小范围于是:
r = mid;③ 所以整个逻辑就是
a[mid] <= x ↓ 不够大 ↓ 往右而:
a[mid] > x ↓ 符合要求 ↓ 左边继续找所以:
if (a[mid] <= x) l = mid + 1; else r = mid;答案:
C
⭐ 一个非常重要的二分口诀
如果题目问:
第一个大于 x
就记:
a[mid] <= x因为:
小于等于 x 的都不要,继续向右。
第10题:二分答案——猜一个“最小载重”
这一题非常有意思。
题目背景:
有很多箱货物,要按原来的顺序分成
days天运输,每天运输连续的一段货物。要求找到能够完成任务的最小载重量。
程序已经有:
check(cap)它可以告诉我们:
“如果卡车载重是
cap,能不能在规定天数内完成?”
题目问二分部分怎么填写。
答案:B。
① 这道题真正的秘密
我们不是直接找答案。
而是在:
猜答案!
例如:
最小可能载重 = 10 最大可能载重 = 100我们猜:
mid = 55然后问:
check(55)② 如果 55 可以完成
说明:
55够用但是问题是:
55 是最小的吗?
不知道!
所以应该:
继续尝试更小也就是:
r = mid;③ 如果 55 不够
说明:
55太小那就必须提高载重量:
往右寻找所以:
l = mid + 1;④ 所以答案是
if (check(mid)) { r = mid; } else { l = mid + 1; }也就是:
check(mid) 成功 ↓ mid 可以 ↓ 答案可能更小 ↓ 往左 check(mid) 失败 ↓ mid 太小 ↓ 往右🧠 这就是“二分答案”
普通二分:
在数组里找数字。
二分答案:
在答案范围里猜答案。
这是非常重要的算法思想。
记忆口诀:
能做成 → 尝试更小。 做不成 → 必须更大。
第11题:归并排序为什么选择<=?
题目是归并排序的合并过程:
while (i <= mid && j <= right) { if (__________________) { temp.push_back(a[i++]); } else { temp.push_back(a[j++]); } }题目要求:
如果希望排序保持稳定,应该怎样填写?
答案:
D:
a[i] <= a[j]原题代码及选项见试卷。
① 什么叫“稳定排序”?
这是这道题最重要的概念。
假设有两个学生:
小明:分数90 小红:分数90他们原来的顺序:
小明 → 小红如果排序以后:
小明 → 小红顺序没有变化。
这叫:
稳定。
② 归并排序正在干什么?
假设左右两边已经排好:
左边: 90(小明) 右边: 90(小红)现在两个 90 一模一样。
程序:
if (a[i] <= a[j])因为:
90 <= 90成立。
于是选择:
左边的小明再选择:
右边的小红最终:
小明 → 小红原来的顺序被保留。
③ 如果写成<呢?
如果:
if (a[i] < a[j])当:
90 < 90是:
false程序就会选择右边:
小红可能变成:
小红 → 小明原来的顺序被改变。
所以为了保证稳定性:
a[i] <= a[j]⭐ 小学生记忆法
归并排序遇到:
左边 = 右边怎么办?
公平一点,先让左边的人过。
所以:
a[i] <= a[j]答案:
D
第12题:快速排序的 partition
题目:
int partition(int a[], int left, int right) { int pivot = a[right]; int i = left - 1; for (int j = left; j < right; j++) { if (__________________) { i++; swap(a[i], a[j]); } } swap(a[i + 1], a[right]); return i + 1; }题目说:
以
a[right]为枢轴,并把不大于枢轴的元素移动到左侧。
答案:
A:
a[j] <= pivot原题代码见试卷。
① pivot 是什么?
pivot可以理解成:
🏰守门员
比如:
5 2 8 3 7假设:
pivot = 7我们希望:
≤7的站到左边。
而:
>7的留在右边。
最终大概形成:
≤7 | 7 | >7② 代码正在扫描谁?
for (int j = left; j < right; j++)也就是:
j ↓ 一个一个检查如果发现:
a[j] <= pivot说明:
“这个小朋友应该站到左边。”
于是:
i++; swap(a[i], a[j]);③ 为什么不是a[j] > pivot?
题目明确说:
不大于 pivot 的放左边。
“不大于”数学语言就是:
≤所以:
a[j] <= pivot答案:
A
第13题:活动安排的经典贪心
题目:
一个教室要安排尽可能多场活动,每场活动有开始时间
start和结束时间end。
采用贪心算法时,正确策略是:
A. 每次选择开始时间最早
B. 每次选择持续时间最短
C. 每次选择参与人数最少
D.按结束时间从早到晚排序,依次选择与已选活动不冲突的活动
答案:
D。
① 为什么不是“开始得最早”?
比如:
活动A:1 ───────── 10 活动B:2 ─ 3 活动C:4 ─ 5 活动D:6 ─ 7如果选择:
开始最早的 A整个教室:
1 ───────── 10后面的 B、C、D 全没了。
只能安排:
1场② 如果选择结束最早的呢?
先看:
B:2 ─ 3结束时间最早。
选 B。
接下来:
C:4 ─ 5不冲突,继续选。
然后:
D:6 ─ 7继续选。
最终:
B → C → D安排了:
3场③ 为什么“结束得早”这么聪明?
因为:
越早结束,就越早把教室空出来。
后面留下的时间越多。
所以:
结束早 ↓ 给后面的活动留下更多空间 ↓ 最终活动数量可能最多这就是经典的:
区间调度问题的贪心策略。
⭐ 贪心口诀
遇到:
“一间教室最多安排多少个互不冲突的活动?”
立刻想到:
按结束时间从小到大排序然后:
能选就选答案:
D
第14题:最大连续子段和
题目给数组:
{-2, 3, -1, 5, -6, 2}函数:
int maxSubArray(const vector &a) { int best = a[0]; int current = a[0]; for (int i = 1; i < (int)a.size(); i++) { current = max(a[i], current + a[i]); best = max(best, current); } return best; }问返回多少?
答案:
C:7
① 什么叫“连续子段”?
比如:
-2 3 -1 5 -6 2可以选择:
3也可以:
3 -1还可以:
3 -1 5但是不能:
3 5因为中间的-1被跳过了。
所以必须:
连续!
② 我们从左往右走
核心代码:
current = max(a[i], current + a[i]);这句话是什么意思?
到了一个新数字:
我要不要把它接在以前的队伍后面?
有两个选择:
选择1:重新开始
a[i]选择2:接着以前的队伍
current + a[i]哪个大,就选哪个。
③ 一步一步算
数组:
-2 3 -1 5 -6 2初始:
current = -2 best = -2来到 3
比较:
3和:
-2 + 3 = 1选择:
3所以:
current = 3 best = 3来到 -1
比较:
-1和:
3 + (-1) = 2选择:
2于是:
current = 2 best = 3来到 5
比较:
5和:
2 + 5 = 7选择:
7于是:
current = 7 best = 7按照题目给出的代码,{-2,3,-1,5,-6,2}的最大连续子段和是7。
因为:
3 + (-1) + 5 = 7继续验证后面也一样:
-6: 7 + (-6) = 1所以:
current = 1 best = 7最后:
2: 1 + 2 = 3最终:
best = 7✅ 第14题答案:C,7
第15题:高精度加法中的进位
最后一道选择题非常经典。
题目给两个高精度整数:
a b而且采用:
低位在前
也就是:
a[0] = 个位 a[1] = 十位 a[2] = 百位代码:
vector add(const vector &a, const vector &b) { vector c; int carry = 0; int n = max(a.size(), b.size()); for (int i = 0; i < n; i++) { int sum = carry; if (i < a.size()) sum += a[i]; if (i < b.size()) sum += b[i]; c.push_back(sum % 10); ____________________ } if (carry) c.push_back(carry); return c; }选项:
A. carry = sum % 10; B. carry = sum; C. carry = sum / 10; D. carry = c[i] / 10;答案:
C:
① 为什么需要高精度?
C++ 的:
int long long都有范围限制。
如果数字特别特别大:
123456789012345678901234567890普通整数装不下。
怎么办?
我们自己用数组保存。
② 为什么“低位在前”?
例如:
1234存成:
a[0] = 4 a[1] = 3 a[2] = 2 a[3] = 1这样做有一个巨大好处:
加法正好从个位开始。
这就和我们小学数学竖式加法完全一样。
③ 看一个简单例子
例如:
58 +67个位:
8 + 7 = 15个位写:
5同时产生:
进位 1所以:
sum = 15那么:
sum % 10得到:
5这就是当前这一位。
而:
sum / 10得到:
1这就是:
下一位要加的进位。
所以:
carry = sum / 10;④%10和/10一定要分清
这是本题最核心的知识。
假设:
sum = 18那么:
sum % 10得到:
8它负责:
留下当前这一位。
而:
sum / 10得到:
1它负责:
把进位送给下一位。
所以:
18 ↙ ↘ %10 /10 ↓ ↓ 8 1 当前位 进位🌟 8~15题最终总结
| 题号 | 知识点 | 答案 | 最重要的一句话 |
|---|---|---|---|
| 8 | 递归 | D | 找到递归出口,再倒着算 |
| 9 | 二分查找 | C | 找第一个>x,判断a[mid] <= x |
| 10 | 二分答案 | B | 能完成→缩小答案;不能完成→增大答案 |
| 11 | 稳定归并排序 | D | 相等时先选左边 |
| 12 | 快速排序 | A | <= pivot放左边 |
| 13 | 贪心 | D | 活动安排优先选结束早的 |
| 14 | 最大子段和 | C | current=max(当前数, 前面+当前数) |
| 15 | 高精度加法 | C | %10留当前位,/10得进位 |
🧠 “考场藏宝图”:
如果考试中看到这些关键词,可以快速联想到算法:
“递归” ↓ 先找出口 “第一个大于” ↓ upper_bound ↓ a[mid] <= x “最小载重量” ↓ 二分答案 “保持稳定” ↓ 相等时左边优先 ↓ <= “pivot / partition” ↓ 快速排序 “最多安排活动” ↓ 结束时间最早 ↓ 贪心 “最大连续子段和” ↓ current / best “高精度加法” ↓ 个位开始 ↓ %10 当前位 /10 进位尤其要把最后这一组口诀记住:
二分:能行就往左,不行就往右。
贪心:活动安排看结束时间。
最大子段和:要么重新开始,要么接着前面。
高精度:%10留自己,/10往前进。