在数组中查找某个给定值,若找到,函数通常返回的是( )。
考点:查找问题与返回值(A1)。
(A1)考点:查找问题与返回值——查找函数找到目标返回其所在下标,失败约定返回 。
解析:本题考查查找问题与返回值。下标能定位元素在数组中的位置,是查找的自然答案; 不是任何合法下标,用作失败标记不会与真实结果混淆。
排除法:A A 以为返回元素值的人没注意「值调用方本来就有」,位置才是新信息;D D 以为返回数组长度的人混淆了查找与计数;C C 以为返回首下标的人没想过目标可能不在开头。
下列最适合用顺序查找(从头到尾逐个比较)的情形是( )。
考点:顺序查找适用场景(A2)。
(A2)考点:顺序查找适用场景——数据无序、查询次数少时,直接顺序扫描最合算。
解析:本题考查顺序查找适用场景。顺序查找零前提、零预处理:数据乱序、只查一两次,排序的预处理成本收不回来,逐个比较反而是最优解。
排除法:D D 选已排序多次查询的场景应该排序后二分;B B 选要求十几次比较的场景只有二分达标;C C 选有序结构的场景同样有更快的选择。
用顺序查找在 个元素的数组中查找某个值,最坏情况下需要比较的次数是( )。
考点:顺序查找最坏次数(A3)。
(A3)考点:顺序查找最坏次数—— 个元素最坏要比较 次。
解析:本题考查顺序查找最坏次数。最坏情形是目标在最后一位或不存在:从下标 一路比到 ,共 次比较,复杂度 。
排除法:B B 答 的人把平均情况错当成最坏情况;D D 答 的是二分的量级,顺序查找没有折半;A A 答 的是最好情况。
顺序查找成功找到目标时(假设目标等概率出现在每个位置),平均比较次数约是( )。
考点:顺序查找平均次数(A4)。
(A4)考点:顺序查找平均次数——目标等概率在各位置时,平均比较约 次。
解析:本题考查顺序查找平均次数。目标在第 位( 起)要比较 次,各位置等概率时平均为 ,约是数组的一半。
排除法:D D 答 的人把最坏当平均;A A 答 的是二分的量级;B B 答 的人把「比较次数」与「所有对组合」混在了一起。
单链表不能高效地使用二分查找,根本原因是( )。
考点:链表只能顺序查找(A5)。
(A5)考点:链表只能顺序查找——链表不支持随机访问,只能沿指针逐个走。
解析:本题考查链表只能顺序查找。数组能一步跳到任意下标,链表想看第 个元素必须从头走 步,任何「跳着查」的策略都无从谈起。
排除法:B B 以为链表元素无序的人没抓住要害——就算排好序,定位不了中点照样无法折半;C C 以为内存不连续就不能存整数的把两件不相干的事扯在一起;D D 以为指针不能比较的没注意比较的是数据域。
C++ 数组 int a[8]; 的合法下标范围是( )。
考点:数组下标从零开始(A6)。
(A6)考点:数组下标从零开始——C++ 数组下标从 到 。
解析:本题考查数组下标从零开始。int a[8] 的合法下标是 至 ,查找循环从 起到 止,数错边界就会漏元素或越界。
排除法:D D 答 到 的人沿用了日常计数习惯;B B 答 到 的人把 也算进去造成越界;C C 答 到 的人两头都数错。
顺序查找的哨兵法把待查值先放到数组末尾空位上,其目的是( )。
考点:哨兵法省边界判断(A7)。
(A7)考点:哨兵法省边界判断——末尾放待查值作哨兵,循环不必判断下标越界。
解析:本题考查哨兵法省边界判断。哨兵保证循环一定能遇到目标值停下,循环条件只剩「值是否相等」一项,每轮少一次越界检查。它不改变最坏比较次数的量级。
排除法:B B 以为哨兵让数组有序的人混淆了查找与排序;A A 以为把最坏变最好的人不了解哨兵只是省判断不省比较;D D 以为减少比较次数上限的人同样高估了它——上限仍是走到哨兵为止。
关于顺序查找,下列说法正确的是( )。
考点:顺序查找综合判断(A8)。
(A8)考点:顺序查找综合判断——顺序查找零前提、代码简单,但最坏比较与元素个数同阶。
解析:本题考查顺序查找综合判断。它对数据无任何要求,这是它最大的优点;代价是线性时间,数据量大时力不从心。
排除法:A A 以为要求数据有序的人把它和二分的前提弄混;C C 以为每次排除一半的人描述的是二分;D D 以为复杂度是对数级的人同样把两种算法的量级张冠李戴。
二分查找的基本思想是( )。
考点:二分的基本思想(B1)。
(B1)考点:二分的基本思想——与中间元素比较,每次把候选区间缩小一半。
解析:本题考查二分的基本思想。有序性保证比较结果能判断目标在左半还是右半,于是每轮安全地丢弃一半候选, 个元素最多 轮出结果。
排除法:B B 选逐个向后移动的人描述的是顺序查找;D D 选分四份同时查找的不是二分的形态;C C 选随机挑元素比较的没有任何「排除一半」的保证。
能对数组使用二分查找,必须满足的前提是( )。
考点:二分的前提条件(B2)。
(B2)考点:二分的前提条件——数组有序且支持随机访问。
解析:本题考查二分的前提条件。有序保证方向判断成立,随机访问保证取中点是常数时间,两者缺一不可。元素可以重复、长度不必是 2 的幂、正负数都无妨。
排除法:D D 选元素互不相同的人加了不存在的前提,重复元素照样可二分;A A 选长度是 的幂的人把「对数次收缩」误解成了对数据规模的形状要求;C C 选都是正数的人给有序性加了无谓限制。
升序数组二分查找中,若 a[mid] < x,下一步应该( )。
考点:比较后的收缩方向(B3)。
(B3)考点:比较后的收缩方向——升序中 a[mid] < x 说明目标只可能在右半。
解析:本题考查比较后的收缩方向。升序数组里 a[mid] 及其左侧都不超过 a[mid],而 a[mid] 已小于 x,整段左侧被一次性排除,下一步 l = mid + 1。
排除法:D D 选去左半的人把不等号方向想反,会背弃目标所在方向;B B 选从头重来的人丢掉了已经获得的信息;A A 选宣布不存在的人把「中点不是目标」误解成「全都不是」。
个元素的有序数组,二分查找的时间复杂度是( )。
考点:二分比较次数量级(B4)。
(B4)考点:二分比较次数量级——二分查找时间复杂度 。
解析:本题考查二分比较次数量级。每轮候选减半:、、……直到剩 个,轮数是 量级,百万元素也只要二十轮上下。
排除法:D D 答 的是顺序查找的量级;B B 答 的是排序的量级——若先排序再查一次,总代价确实由排序主导;C C 答 的人忘了查找至少要比较一次。
在 万(约 )个元素的有序数组中查找,二分查找最多比较约 次,顺序查找最坏要比较( )。
考点:二分与顺序速度对比(B5)。
(B5)考点:二分与顺序速度对比——百万元素二分约 20 次,顺序最坏 100 万次。
解析:本题考查二分与顺序速度对比。 万,二分最多比较约 次;顺序查找最坏要把每个元素都看一遍,即 万次,两者相差五万倍。
排除法:D D 答 次的人把二分的次数错记到顺序头上;C C 答 次的人量级估算失真;A A 认为在 万与 万之间无法确定的人不了解最坏情况的上界是确定的——最多 万次就是定论。
二分查找每一轮循环真正维护的东西是( )。
考点:二分的本质是收缩(B6)。
(B6)考点:二分的本质是收缩——每轮维护一个可能包含目标的候选区间并减半。
解析:本题考查二分的本质是收缩。l、r 圈出候选范围,每轮用一次比较把范围砍半,区间空了(没找到)或命中为止。抓住这个不变量,各种变体都是同一骨架。
排除法:D D 选计数器的人把循环变量当主角;B B 选维护有序性的人没注意有序是前提不是被维护的东西;A A 选目标副本的人凭空想象了一个不存在的数据。
对单链表强行使用「二分」思想,效率提不上去,是因为( )。
考点:链表不能高效二分(B7)。
(B7)考点:链表不能高效二分——定位链表中间节点本身就要线性时间。
解析:本题考查链表不能高效二分。二分省时间的关键是「一步取到中点」,链表取中点要走半条链,省下的比较全花在走链上,总时间仍是线性。
排除法:D D 选内存溢出的人编造了不存在的限制;A A 选无法比较大小的人没注意节点数据域照样可比;C C 选中间节点不存在的人想当然——中间节点存在,只是到不了。
关于二分查找的局限,正确的是( )。
考点:二分查找的局限(B8)。
(B8)考点:二分查找的局限——依赖有序性,预处理可能不划算;对重复元素照样可用。
解析:本题考查二分查找的局限。数据无序要先排序(),只查一次时不如直接扫;查得多次时排序一次反复受益。重复元素、小数、不存在的目标都能正确处理。
排除法:B B 以为只能查整数的人没见过浮点二分;C C 以为目标必须存在的人忘了失败返回 正是标准设计;D D 以为重复元素不行的人不了解边界变体正是为此而生。
二分中点常写成 mid = l + (r - l) / 2 而不是 mid = (l + r) / 2,原因是( )。
考点:中点写法防溢出(C1)。
(C1)考点:中点写法防溢出——l + (r - l) / 2 与 (l + r) / 2 数学等价且不会溢出。
解析:本题考查中点写法防溢出。l、r 都接近 亿时相加超出 int 上限,结果变负;先做减法差值很小,加回 l 不会越界。教学口径:两个写法算出的中点相同,差别只在安全性。
排除法:D D 以为更快的人把安全性优势错记成功效优势;B B 以为中点不同的人没推导过两式恒等;A A 以为自动上取整的人不知道整数除法始终向下取整,与写法无关。
闭区间写法的二分查找中,循环条件用 while (l <= r),原因是( )。
考点:闭区间循环条件(C2)。
(C2)考点:闭区间循环条件——闭区间写法在 l <= r 时区间非空,仍需继续。
解析:本题考查闭区间循环条件。闭区间 [l, r] 在 l == r 时还剩一个元素没检查,必须再进一轮;写成 l < r 会漏判这最后一个候选。区间空(l > r)才是终止的充分条件。
排除法:C C 认为两条件等价的人在 l == r 处会漏元素;B B 认为少等号会死循环的人方向说反了——少等号是提前退出,多查才是死循环风险来源之一;D D 以为是为奇数长度服务的人把循环条件与取中点混为一谈。
比较后收缩区间写 l = mid + 1 或 r = mid - 1 而不是 l = mid 或 r = mid,原因是( )。
考点:收缩时排除中点(C3)。
(C3)考点:收缩时排除中点——a[mid] 比较过就不再进候选,mid+1、mid-1 保证区间严格缩小。
解析:本题考查收缩时排除中点。既然已经确认 a[mid] 不是目标(或目标不在其一侧),把 mid 留在区间里只会白比一次甚至原地踏步;严格缩小也是循环必然终止的保证。
排除法:D D 以为缩得更快的人没注意两种写法每轮同样只缩小约一半;A A 以为编译错误的人知道得太少——l = mid 语法完全合法,坑在逻辑;C C 以为是习惯写法的人低估了它——不留 mid 正是防死循环的关键之一。
二分查找函数常把结果变量初始化为 ,其含义是( )。
考点:查找失败返回负一(C4)。
(C4)考点:查找失败返回负一—— 不是合法下标,是标准的失败哨兵值。
解析:本题考查查找失败返回负一。合法下标从 起, 绝不会与真实位置撞车,调用方拿到 即知目标不存在。res 初值设 的用意相同。
排除法:D D 以为表示从末尾找的人把哨兵值误当成了方向;C C 以为表示成功的人把含义整个弄反;A A 以为是首下标的人忘了下标从 不从 起。
01int bsearch(int a[], int l, int r, int x) { 02 if (l > r) return -1; 03 int mid = (l + r) / 2; 04 if (a[mid] == x) return mid; 05 if (a[mid] < x) return bsearch(a, mid + 1, r, x); 06 return bsearch(a, l, mid - 1, x); 07} 08int main() { 09 int a[] = {3, 7, 11, 15, 19}; 10 cout << bsearch(a, 0, 4, 11); 11 return 0; 12}
输出是( )。
考点:递归二分查找(C5)。
(C5)考点:递归二分查找——递归版以 l > r 为终止条件,每层只在半边继续。
解析:本题考查递归二分查找。找 :区间 中点 恰是 ,直接返回 。递归版与迭代版是同一逻辑的两种组织方式,深度为 ,不会爆栈。
排除法:C C 答 的人混淆了「下标」与「值」;A A 答 的人没算出中点首跳即命中;B B 答 的人把数组末尾当成了答案。
01int a[] = {3, 7, 11, 15, 19, 23}; 02int x = 5; 03int l = 0, r = 5; 04while (l <= r) { 05 int mid = (l + r) / 2; 06 if (a[mid] == x) break; 07 else if (a[mid] < x) l = mid + 1; 08 else r = mid - 1; 09} 10cout << l << " " << r;
输出是( )。
考点:失败时区间交错(C6)。
(C6)考点:失败时区间交错——查找失败时 l 最终越过 r,输出 1 0。
解析:本题考查失败时区间交错。找不存在的 :中点 (值 )偏大,r=1;中点 (值 )偏小,l=1;中点 (值 )偏大,r=0,l > r 退出。l 停在第一个大于目标的位置、r 停在最后一个小于目标的位置,交错即「夹缝」。
排除法:B B 答 0 5 的人以为区间不动;A A 答 2 1 的人追踪轮次错一格;C C 答 0 0 的人漏了最后一轮收缩。
在 个元素的有序数组中二分查找,第一次比较结束后,候选元素最多还剩( )。
考点:一次比较剩一半(C7)。
(C7)考点:一次比较剩一半——1000 个元素第一次比较后候选至多剩 500 个。
解析:本题考查一次比较剩一半。 个候选与中点比较后,目标只可能在左半或右半之一,较大的一半是 个(奇数个时中点本身被排除)。再看一轮剩 个,直观看见对数收缩。
排除法:C C 答 的人以为比较只能排除一个元素,那是顺序查找;A A 答 的人少算了一轮;D D 答 的人以为比较不缩小范围。
要找升序数组中第一个等于 x 的位置,二分命中 a[mid] == x 后正确的做法是( )。
考点:第一个等于的求法(D1)。
(D1)考点:第一个等于的求法——命中后记下位置继续向左压缩。
解析:本题考查第一个等于的求法。a[mid] == x 只说明找到一个,未必最靠前;把 r 挪到 mid - 1 继续在左侧找更早的命中,循环结束时的记录值就是第一个。这种「记下再压缩」是边界二分的通用骨架。
排除法:D D 选立即返回的人会被重复元素坑——中点可能落在重复段中间;A A 选向右找的人方向反了,越找越靠后;B B 选返回 的人臆断了「第一个必在开头」。
要找升序数组中最后一个等于 x 的位置,二分命中后正确的做法是( )。
考点:最后一个等于的求法(D2)。
(D2)考点:最后一个等于的求法——命中后记下位置继续向右压缩。
解析:本题考查最后一个等于的求法。与 D1 镜像:把 l 挪到 mid + 1 向右试探更晚的命中,记录值最终停在最后一个等于处。
排除法:D D 选立即返回的人同样会被重复段坑;C C 选向左找的人方向反了;B B 选返回长度减一的人以为目标必在末尾,纯属臆断。
在升序数组中,lower_bound 查找返回的位置语义是( )。
考点:lower_bound 语义(D3)。
(D3)考点:lower_bound 语义——返回第一个大于等于 x 的位置。
解析:本题考查 lower_bound 语义。它是「第一个不小于」的分界点:x 存在时是首个等于处,不存在时是首个大于处,因此同时兼任「插入位置」。手写实现正是 J6 的 a[mid] >= x 收缩法。
排除法:A A 选第一个大于的人把它与 upper_bound 弄混,差一个等号;D D 选第一个等于的人在 x 不存在时无位置可返;C C 选最后一个小于的人方向完全反了。
在升序数组中,upper_bound 查找返回的位置语义是( )。
考点:upper_bound 语义(D4)。
(D4)考点:upper_bound 语义——返回第一个大于 x 的位置。
解析:本题考查 upper_bound 语义。它越过所有等于 x 的元素,给出严格大于的分界;与 lower_bound 相减得到 x 的出现个数(D8 的基础)。
排除法:D D 选第一个大于等于的是 lower_bound 的语义;B B 选最后一个等于的人在 x 不存在时同样无位置可返;C C 选最后一个小于等于的人把开区间边界想反。
「找最后一个满足条件的位置」的二分中,中点常写成 mid = (l + r + 1) / 2 并配 l = mid,加一的目的是( )。
考点:上取整中点防死循环(D5)。
(D5)考点:上取整中点防死循环——l = mid 配 (l + r + 1) / 2,两元素区间时中点取右边。
解析:本题考查上取整中点防死循环。区间剩 时,下取整中点恒为 ,若此时执行 l = mid 区间纹丝不动,死循环;加一后中点变 ,l = mid 必然推进。口诀:l = mid 配上取整,r = mid 配下取整。
排除法:C C 以为收缩更快的人没抓住要害——加一为的是不死循环而非性能;B B 以为更靠左的人正好说反,加一是向右偏;D D 以为抵除法误差的人不知道整数除法没有误差,只有截断方向。
条件 P(i) 随下标 i 单调(前段全假、后段全真)时,找第一个 P(i) 为真的下标,可以用二分的原因是( )。
考点:二分下标找首个满足(D6)。
(D6)考点:二分下标找首个满足——条件真假单调时,判断结果同样能把下标区间砍半。
解析:本题考查二分下标找首个满足。P(mid) 为假说明分界在右侧(左侧连 mid 都假),为真说明分界在 mid 或其左——每次判断淘汰一半下标, 次锁定真假分界。lower_bound 是它 P(i) = (a[i] >= x) 的特例。
排除法:A A 以为任何条件都能二分的人没注意单调性是命根子,条件乱跳时判断不能淘汰任何一侧;B B 以为与条件真假无关的人只看到下标有序,却忘了淘汰依据来自条件;D D 以为不能找条件边界的人低估了这一框架的威力。
把 x 插入升序数组并保持有序,插入点的位置恰好等于( )。
考点:插入位置的语义(D7)。
(D7)考点:插入位置的语义——保持有序的插入点等于第一个大于等于 x 的位置。
解析:本题考查插入位置的语义。插在该位置前面,左侧全部小于 x、右侧全部大于等于 x,有序得以保持;这正是 lower_bound 的位置。x 大于所有元素时该位置是末尾,同样成立。
排除法:A A 选数组末尾的人没考虑 x 较小应插在中间的情形;C C 选第一个小于 x 的位置的人插错了侧,会把 x 放到比它小的元素左边;D D 选正中的人把「插入」当成了「对半」。
升序数组中 x 出现的次数,可以用两次边界二分直接算出,公式是( )。
考点:出现次数等于差值(D8)。
(D8)考点:出现次数等于差值——出现次数 = upper 位置减 lower 位置。
解析:本题考查出现次数等于差值。lower 停在第一个 、upper 停在第一个 ,两位置之间的元素全部等于 ,长度恰为差值;x 不存在时两位置重合,差值为 ,公式依旧成立。O2 是它的完整代码。
排除法:D D 选 lower 减一的人在 x 不存在时会算出负数或错值;A A 选长度减 upper 的算出的是「大于 x 的元素个数」;C C 选无法算出的人没见过两次边界二分的组合拳。
「二分答案」指的是( )。
考点:二分答案的定义(E1)。
(E1)考点:二分答案的定义——对答案取值范围二分,配判定函数锁定可行值。
解析:本题考查二分答案的定义。当「验证一个候选答案」比「直接构造答案」容易得多时,把候选范围当有序数组二分:判定可行就往更优方向试,不可行就退回来,对数轮收敛。
排除法:D D 选对下标二分的人描述的是普通二分查找;A A 选平均分两半相加的人编造了不存在的算法;B B 选随机猜两次的人完全脱离二分的确定性框架。
二分答案成立的前提是答案具有单调性,即( )。
考点:单调性前提(E2)。
(E2)考点:单调性前提——可行性随答案取值单调变化,二分才有淘汰依据。
解析:本题考查单调性前提。「可行」与「不可行」在值域上各占连续的一段(如可行 | 不可行),判定一次就能淘汰半段值域;若可行集间断分布,判定结果无淘汰力,二分失效。
排除法:B B 以为答案必须是整数的人把「常为整数」当成了「必须」;A A 以为可行解唯一的人没想到恰恰是要在大量可行解中找最优;C C 以为范围不超过一百万的人把工程习惯当成了数学前提。
求「最大的可行答案」的二分答案框架中,check(mid) 为真时应执行( )。
考点:基本框架(E3)。
(E3)考点:基本框架——求最大可行答案:可行则记录并向大试,不可行则回退。
解析:本题考查基本框架。check(mid) 为真说明 mid 可行,但要试探能不能更大,故 ans = mid; l = mid + 1;为假说明太大,r = mid - 1。循环结束 ans 即最大可行值。求最小可行答案则方向全部对称反转。
排除法:D D 选向更小试的人把「最大」的方向弄反,会收敛到下界;B B 选立即输出的人放弃了继续优化的机会;C C 选递归对半的人把二分答案与分治混为一谈。
下列题目特征中最提示「这题该二分答案」的是( )。
考点:题目特征(E4)。
(E4)考点:题目特征——「最大的最小值 / 最小的最大值」加单调可行性是二分答案的信号。
解析:本题考查题目特征。这类问法天然定义了「候选值——可行性」的单调关系:把阈值调宽松(最短距离调小、切割边长调小)总更容易满足,于是可二分。看到该特征先想 check 怎么写。
排除法:A A 选求数组所有元素之和的人面对的是简单统计,没有阈值可行性的结构;C C 选判断字符串是否回文的人面对的是字符串性质判定,同样无单调候选值可言;B B 选输出数组全排列的人面对的是枚举问题,组合空间里没有「可行段分界」可二分。
二分答案中 check(v) 函数的职责是( )。
考点:check 函数的作用(E5)。
(E5)考点:check 函数的作用——判定候选答案可行性,通常一次贪心或扫描完成。
解析:本题考查 check 函数的作用。二分答案把「求最优」拆成「验证给定的值行不行」:check 内部往往是一个线性扫描的贪心(数够不够、截够不够),复杂度 ;整体 。
排除法:B B 选计算最终答案的人弄反了分工——check 只判定不算最优;A A 选排序的人把预处理当成了判定;D D 选生成数据的人完全脱离语境。
二分答案时答案变量常声明为 long long,典型原因是( )。
考点:答案用 long long(E6)。
(E6)考点:答案用 long long——判定中的乘积或总和可能超出 int 范围。
解析:本题考查答案用 long long。mid 本身也许不大,但 mid * mid(K4 的平方判断)、总长度、总块数这类统计量轻易突破 亿;答案变量与中间量一起开 long long 是稳妥习惯。
排除法:D D 以为更快的人把安全当成速度;B B 以为循环计数必须长整型的人混淆了不同的变量;C C 以为 int 不能比较的人夸大了限制——比较没问题,溢出才有问题。
二分答案与普通二分查找的关系,最准确的说法是( )。
考点:与二分查找的关系(E7)。
(E7)考点:与二分查找的关系——同一「砍一半」骨架,二分的对象从下标域换成答案值域。
解析:本题考查与二分查找的关系。二分查找在有序下标上找目标,二分答案在单调可行的值域上找最优,两者共享「判定一次淘汰一半」的灵魂;把「值域」看成「虚拟的有序数组」即可互相解释。
排除法:B B 选毫无共同点的人只见其形不见其神;C C 选多线程版本的人凭空发挥;A A 选必须先二分答案的人把依赖关系编反了——普通二分查找根本用不到二分答案。
砍树问题:给每棵树一个高度,选一个统一的砍伐高度,高于它的部分被截下,要求截得的木材总量不少于需求量,且砍伐高度尽量高。答案随砍伐高度的变化是( )。
考点:砍树问题模型(F1)。
(F1)考点:砍树问题模型——木材总量随砍伐高度单调不增,可二分高度。
解析:本题考查砍树问题模型。高度定得越高,每棵树截下的部分越短,总量只减不增;「总量 ≥ 需求」的可行性随之单调(低处全可行、高处全不可行),分界点即最高可行高度。check 是一次线性求和(L1)。
排除法:B B 以为总量与高度无关的人没算过高处截不到木材;A A 以为单调递增的人把方向想反,高度越高截得越多显然不真;C C 以为先增后减的人给虚构的形状硬安了二分不可行的结论——单调恰是二分的通行证。
跳石头问题:从起点到终点有若干石头,最多移走 块,要使剩余石头间(含起终点)的最短跳跃距离尽量大。二分的对象是( )。
考点:跳石头问题模型(F2)。
(F2)考点:跳石头问题模型——二分最短跳跃距离,距离越大需要移走的石头越多。
解析:本题考查跳石头问题模型。要求「任意相邻保留位置距离 ≥ mid」,mid 定得越大,被迫移走的石头越多,「移走数 ≤ M」的可行性单调递减,分界即最大可行距离。check 用一次贪心扫描(F7、L3)。
排除法:C C 选二分石头编号的人没注意编号不承载「距离阈值—可行性」的单调关系;B B 选石头总块数的人把约束条件当成了二分对象;D D 选起终点长度的人混淆了常量与变量。
分巧克力问题:把多块矩形巧克力切成若干正方形小份(每块只能按一种边长切),要切出至少 份且边长尽量大。随着切的边长增大,能切出的总份数( )。
考点:分巧克力问题模型(F3)。
(F3)考点:分巧克力问题模型——总份数随切割边长单调不增。
解析:本题考查分巧克力问题模型。边长越大,每块能切出的 份数越少,总份数单调不增;「份数 ≥ K」的可行段在左侧,分界即最大边长。check 是逐块求和(L4)。
排除法:C C 选单调递增的人方向反了,边长越大越切不出份数;A A 选保持不变的人没注意每块边长一旦不够整除份数直接掉零;D D 选先增后减的人同样虚构了非单调形状。
条件 mid * mid <= 30 随 mid 增大由真变假,找最大的满足条件的 mid,这类问题可以直接二分,原因是( )。
考点:单调函数最大值(F4)。
(F4)考点:单调函数最大值——条件真假单调变化,分界点即答案。
解析:本题考查单调函数最大值。mid * mid <= 30 在 处为真、 处为假,真假分界就是最大满足值 (L5 实测)。整数平方根(K4)同骨架:mid * mid <= n 找最大的 mid。
排除法:D D 以为任何不等式都能二分的人忽略了必须一侧恒真一侧恒假;A A 以为 是 2 的幂才行的给前提加了不存在的条件;C C 以为满足的值唯一的人没想到 到 都满足,要的是其中最大的。
求最大可行答案时,在 check(mid) 为真的分支里写 ans = mid,其作用是( )。
考点:ans 记录法(F5)。
(F5)考点:ans 记录法——可行即记录并继续向优,循环结束 ans 即最优可行值。
解析:本题考查 ans 记录法。求最大值方向:每次可行都记 ans = mid 再右推,之后即便全不可行,ans 保留着最后(也是最大)的可行值。它是二分答案框架里防「收敛点不是可行点」的保险。
排除法:D D 选清零重来的人没注意记录的正是要留的东西;B B 选标记失败的人把成功分支的语义弄反;C C 选提前退出的人放弃了继续向优的机会,得到的不是最大可行值。
二分「砍树高度」时,右界 r 的合理初值通常取( )。
考点:上界的设定(F6)。
(F6)考点:上界的设定——右界取所有树高的最大值,再高截不到任何木材。
解析:本题考查上界的设定。砍伐高度超过最高的树时一根木材都得不到,必然不可行,再大的候选毫无意义;取「最大树高」既不漏解又最小化二分轮数。下界通常取 。
排除法:B B 选树高之和的人把「高度」与「总量」量纲弄混;D D 选需求量的人把约束当成了范围;A A 选棵数的人同样量纲错位——上界要罩住的是高度取值。
跳石头问题的 check(mid) 里,从起点向终点扫描,遇到与「上一块保留位置」距离小于 mid 的石头就移走,这属于( )。
考点:check 里的贪心统计(F7)。
(F7)考点:check 里的贪心统计——一次线性扫描,能不移就不移,使移走数最少。
解析:本题考查 check 里的贪心统计。判定 mid 是否可行时从左到右扫:与上一保留位置距离不足 mid 的石头必须移走,其余保留——这样移走数最少;若最少的移走数都不超过 M,则可行。贪心的正确性来自「保留越早越给后面留余地」。
排除法:A A 选动态规划的人高估了判定难度,线性贪心足够;D D 选排序算法的人把预处理当成了判定本体;B B 选递归回溯的人没注意到判定不需要枚举方案,只要最优计数。
用二分法求 :初始区间 ,每步取中点,若中点的平方不超过 就把左端移到中点,否则把右端移到中点。这个方法能锁定平方根的原因是( )。
考点:实数二分求平方根(G1)。
(G1)考点:实数二分求平方根——平方不超过 2 随取值增大由真变假,根在分界。
解析:本题考查实数二分求平方根。区间 ,中点平方小于等于 就把左端推过去,否则右端退回来,每轮根被夹在宽度减半的区间里(M1 是前三轮的实测)。2022 年阅读真题正是这一模型叠加迭代加细。
排除法:C C 以为 是质数才能这样二分的人给前提加了无关条件,任何正实数都同理;A A 以为区间端点是整数才行的没注意端点第二轮起就是小数;B B 以为平方不改变单调方向的人恰恰说反了—— 递增正是「真变假」单调性的来源,正是它能二分。
浮点二分常用 while (r - l > 1e-6) 作为循环条件,终止的含义是( )。
考点:精度终止条件(G2)。
(G2)考点:精度终止条件——区间宽度小于 eps 时两端皆可作为近似答案。
解析:本题考查精度终止条件。浮点没有「恰好相等」可指望,改为把答案夹进足够窄的区间:r - l > 1e-6 继续缩,停止时 、 相差不足精度要求,输出任一端(或中点)误差可控。
排除法:B B 以为答案恰等于中点的人不了解停止时中点只是近似;A A 以为固定次数的人描述的是另一种等价写法(G3);D D 以为答案是整数的人没注意这是浮点二分。
浮点二分也常直接 for (int i = 0; i < 100; i++) 循环一百次,原因是( )。
考点:固定次数迭代(G3)。
(G3)考点:固定次数迭代——一百轮减半后区间宽度小于任何实际需要的精度。
解析:本题考查固定次数迭代。初始宽度再大, 轮后宽度除以 ,远小于任何输出精度要求;用固定次数代替浮点比较,回避了 eps 选得不好导致的边界纠缠,是竞赛常用等价写法。
排除法:C C 以为一百次后变精确值的人夸大了迭代效果,仍是近似;B B 以为与溢出有关的人把两个不相干的问题扯在一起;D D 以为要输出一百位的人没注意题目精度通常只要求几位小数。
用二分求方程 的根,要求 在区间 两端点处的函数值( )。
考点:二分求方程根(G4)。
(G4)考点:二分求方程根——两端函数值异号保证根在区间内,按中点符号收缩。
解析:本题考查二分求方程根。连续函数在 、 异号时必穿越零点;取中点,其符号与哪端相同就把该端移到中点,根始终被夹在异号区间内(M3 用 实测四轮)。
排除法:C C 选同号的人不知道同号不保证有根,二分无从谈起;B B 选都为零的人把特殊情况当成了前提;A A 选都是正数的人同样丢失了根存在的保证。
浮点二分答案要求「保留 位小数」,循环精度 eps 通常取得比输出精度更小(如 ),原因是( )。
考点:浮点输出精度(G5)。
(G5)考点:浮点输出精度——eps 要比输出精度小,防四舍五入错位。
解析:本题考查浮点输出精度。输出三位小数时,若答案近似值与真值相差半分位以上,四舍五入可能差一位;把 eps 取到 ,误差远小于 的半分位阈值,输出才稳。
排除法:C C 以为 eps 越大越慢的人把大小与快慢的因果关系记反——eps 越大停得越早;A A 以为输出位数由 eps 决定的人不知道输出位数由格式控制,eps 只保准确性;B B 以为浮点存不了三位小数的人夸大了浮点的局限。
求 ( 为正实数)时,初始区间取 在 时会出问题,更稳妥的右界是( )。
考点:浮点二分起点(G6)。
(G6)考点:浮点二分起点——右界取 max(x, 1),覆盖 0 到 1 之间开方变大的情形。
解析:本题考查浮点二分起点。 时 ,若右界只取 ,根被排除在区间外,二分永远夹不到根;右界至少取 ( 是分水岭)才能覆盖所有正实数。
排除法:A A 选 的人在 极小(如 )时 仍小于根,区间照样不含根;D D 选 的人把右界改得更小,错上加错;C C 选 的人在 时 比 还小,同样失效。
二分查找每轮能把候选元素排除一半,依赖的两个事实是( )。
考点:每次排除一半的原因(H1)。
(H1)考点:每次排除一半的原因——有序给出方向判断,随机访问给出常数取中点。
解析:本题考查每次排除一半的原因。有序让「中点偏小/偏大」直接翻译成「目标在右/左」;随机访问让取中点是一步到位的 。两者合起来才有「比较一次、安全丢弃一半」。
排除法:D D 选元素互不相同与长度为偶数的人加错了前提,重复与奇数长度无妨;A A 选目标一定存在的忘了失败情形同样每轮减半;B B 选不允许重复的人与 H 的正确口径相反——重复元素恰是边界变体的用武之地。
约 万元素的有序数组,二分查找最多比较的次数约为( )。
考点:对数规模估算(H2)。
(H2)考点:对数规模估算——百万元素二分最多约 20 次。
解析:本题考查对数规模估算。 万,二十次减半足以把百万候选缩到一到两个;每翻十倍元素只多三四次比较,这是对数的从容。
排除法:C C 答 次的人把对数量级当成了百分位;A A 答 次的人高估了三个数量级;D D 答 万的人把顺序查找的最坏次数错安到二分头上。
只对无序数组做一次查找,选择顺序查找而不是「先排序再二分」的理由是( )。
考点:单次查找的选择(H3)。
(H3)考点:单次查找的选择——只查一次时排序不划算,直接顺序扫描。
解析:本题考查单次查找的选择。排序要 ,一次顺序扫描只要 ;为一次查询先付排序成本是「杀鸡用牛刀且刀钱更贵」。查的次数多起来后,排序一次的成本才能被摊薄。
排除法:B B 以为顺序一定更快的人没注意多次查询时二分摊销更优;C C 以为排序会破坏数据的人混淆了重排与破坏——数据还在,只是有序了;A A 以为二分不能用在有序数组上的人把前提说反了。
对同一批数据要做上万次「查某个值在不在」的查询,常见的高效方案是( )。
考点:多次查询的选择(H4)。
(H4)考点:多次查询的选择——排序一次,之后每次二分对数代价。
解析:本题考查多次查询的选择。上万次查询若每次顺序扫描要 ;先花 排序,之后每次 ,总量 远小。O1 是「排序后二分」的代码形态。
排除法:C C 选每次顺序扫描的人在多次查询下代价线性叠加;B B 选每次先打乱的人不但无益反而破坏可二分性;A A 选打印人工找的人脱离了算法讨论。
个已排序的数据元素,采用折半查找,最大比较次数是( )。
考点:折半最大比较次数(H5)。
(H5)考点:折半最大比较次数——n 个元素最大比较 ⌊log₂n⌋+1 次。
解析:本题考查折半最大比较次数。 个元素:,最坏走满 层。口诀「向下取整加一」; 个元素同法得 次。此考法在近年真题中两次出现,值得背熟推法。
排除法:A A 答 的人可能照搬了 元素的答案,没按本题 元素重算;B B 答 的人只取了 的整数部分漏加一;D D 答 的人把顺序查找一半的量级错安过来。
01int a[] = {4, 7, 2, 7, 9}; 02int x = 7; 03int pos = -1; 04for (int i = 0; i < 5; i++) 05 if (a[i] == x) { pos = i; break; } 06cout << pos;
输出是( )。
考点:顺序查找输出下标(I1)。
(I1)考点:顺序查找输出下标——带 break 的扫描输出第一个匹配下标。
解析:本题考查顺序查找输出下标。 在下标 首先命中,break 立即停住,输出 ;后面下标 的 不再看。break 保证「最早命中」语义。
排除法:C C 答 的人忽略了 break,以为会扫完取最后;A A 答 的人混淆了下标与值;D D 答 的人没看到首个元素就命中不了——第二个元素就是 。
01int a[] = {3, 1, 4, 1, 5}; 02int x = 9; 03int pos = -1; 04for (int i = 0; i < 5; i++) 05 if (a[i] == x) pos = i; 06cout << pos;
输出是( )。
考点:查找失败返回负一(I2)。
(I2)考点:查找失败返回负一——目标不存在时 pos 保持初值 。
解析:本题考查查找失败返回负一。 不在数组中,五轮比较全落空,pos 从未被赋值,保持 输出。这题同时提醒:无 break 的循环扫完整个数组也一无所获。
排除法:D D 答 的人以为失败返回首下标;B B 答 的人把「扫到最后」误当「返回最后下标」;C C 答 的人输出成了查找目标本身。
01int a[] = {2, 5, 2, 3, 2, 8}; 02int cnt = 0; 03for (int i = 0; i < 6; i++) 04 if (a[i] == 2) cnt++; 05cout << cnt;
输出是( )。
考点:统计出现次数(I3)。
(I3)考点:统计出现次数——不 break 的顺序扫描逐个计数。
解析:本题考查统计出现次数。 出现在下标 、、 共 处,循环必须走完全程,输出 。计数场景 break 反而漏计。
排除法:B B 答 的人以为命中一次就完事;C C 答 的人把数组长度当成了次数;D D 答 的人漏数了中间那个 。
01int a[] = {6, 3, 6, 6, 1}; 02int pos = -1; 03for (int i = 0; i < 5; i++) 04 if (a[i] == 6) pos = i; 05cout << pos;
与第 57 题不同,这段循环没有 break,输出是( )。
考点:不停时取最后匹配(I4)。
(I4)考点:不停时取最后匹配——无 break 的循环每次命中覆盖 pos,留下最后一个。
解析:本题考查不停时取最后匹配。 在下标 、、 出现,最后覆盖发生在下标 ,输出 。有无 break 决定「第一个」还是「最后一个」,一字之差语义全变。
排除法:A A 答 的人按 break 的语义理解了无 break 的代码;B B 答 的人没看到数组里有 ;C C 答 的人又把值当下标输出。
01int a[] = {5, 9, 3, 9, 4}; 02int k = 0; 03for (int i = 1; i < 5; i++) 04 if (a[i] > a[k]) k = i; 05cout << k << " " << a[k];
输出是( )。
考点:打擂台找最大位置(I5)。
(I5)考点:打擂台找最大位置——严格大于才换擂主,并列保留最先。
解析:本题考查打擂台找最大位置。 在下标 与 并列最大,a[i] > a[k] 是严格大于,下标 的 不再换擂,输出 1 9。若想取最后一个最大值要改成 >=。
排除法:D D 答 3 9 的人默认了并列换擂,严格比较恰恰不换;B B 答 1 5 的人输出的擂主初值是首元素旧值,擂台在第一轮就换成了 ;C C 答 4 9 的人臆断了数组末尾,末元素是 上不了擂。
01int a[] = {2, 5, 8, 12, 16, 23, 38, 56, 72, 91}; 02int x = 23; 03int l = 0, r = 9; 04int res = -1; 05while (l <= r) { 06 int mid = (l + r) / 2; 07 if (a[mid] == x) { res = mid; break; } 08 else if (a[mid] < x) l = mid + 1; 09 else r = mid - 1; 10} 11cout << res;
输出是( )。
考点:标准二分执行(J1)。
(J1)考点:标准二分执行——追踪三轮锁定下标 5。
解析:本题考查标准二分执行。找 :区间 中点 (值 )推左界到 ;区间 中点 (值 )退右界到 ;区间 中点 (值 )命中,输出 。
排除法:A A 答 的人输出成了元素值而非下标;D D 答 的人停在第一轮中点没继续收缩;B B 答 的人没追踪完, 就在数组里。
01int a[] = {2, 5, 8, 12, 16, 23, 38, 56, 72, 91}; 02int x = 24; 03int l = 0, r = 9; 04int res = -1; 05while (l <= r) { 06 int mid = (l + r) / 2; 07 if (a[mid] == x) { res = mid; break; } 08 else if (a[mid] < x) l = mid + 1; 09 else r = mid - 1; 10} 11cout << res;
输出是( )。
考点:二分失败执行(J2)。
(J2)考点:二分失败执行——目标不存在时区间交错退出,返回初值 。
解析:本题考查二分失败执行。找 :中点 ()左界推到 ;中点 ()右界退到 ;中点 ()左界推到 ;中点 ()右界退到 ,l > r 退出,输出 。
排除法:D D 答 的人停在中间某一轮以为会命中;B B 答 的人把最后的交错界当成了答案下标;A A 答 的人以为失败返回首下标。
01int a[] = {91, 72, 56, 38, 23, 16, 12, 8, 5, 2}; 02int x = 72; 03int l = 0, r = 9, res = -1; 04while (l <= r) { 05 int mid = (l + r) / 2; 06 if (a[mid] == x) { res = mid; break; } 07 else if (a[mid] > x) l = mid + 1; 08 else r = mid - 1; 09} 10cout << res;
输出是( )。
考点:降序数组二分(J3)。
(J3)考点:降序数组二分——收缩方向随有序方向反转。
解析:本题考查降序数组二分。降序中「更大」在左侧:找 ,中点 (值 )说明目标在左侧,退右界到 ;中点 (值 )命中,输出 。方向反了就全盘皆错(P3)。
排除法:B B 答 的人按升序方向收缩走进了死胡同;C C 答 的人方向反了自然找不到;D D 答 的人又是值与下标不分。
01int a[] = {1, 3, 5, 7, 9, 11, 13, 15}; 02int x = 15; 03int l = 0, r = 7; 04while (l <= r) { 05 int mid = (l + r) / 2; 06 cout << mid; 07 if (a[mid] == x) break; 08 else if (a[mid] < x) l = mid + 1; 09 else r = mid - 1; 10}
输出是( )。
考点:输出中点序列(J4)。
(J4)考点:输出中点序列——每轮中点依次是 3、5、6、7。
解析:本题考查输出中点序列。找末元素 :中点 ()左界到 ;中点 ()左界到 ;中点 ()左界到 ;中点 ()命中。输出连起来是 3567。找末元素时中点一路右挪,是对数收缩的直观样子。
排除法:B B 答 367 的人漏了首轮的 ;A A 答 35 的人以为两轮就停;C C 答 7 的人以为一步就猜中末尾。
01int a[] = {2, 5, 8, 12, 16, 23, 38, 56, 72, 91}; 02int x = 91; 03int l = 0, r = 9, cnt = 0; 04while (l <= r) { 05 int mid = (l + r) / 2; 06 cnt++; 07 if (a[mid] == x) break; 08 else if (a[mid] < x) l = mid + 1; 09 else r = mid - 1; 10} 11cout << cnt;
输出是( )。
考点:比较次数统计(J5)。
(J5)考点:比较次数统计——找末元素恰好走了 4 轮。
解析:本题考查比较次数统计。找 :中点 ,前三轮都偏小推左界,第四轮中点 恰是 ,cnt 停在 。十个元素的查找最多也就四轮,印证 向上取整。
排除法:A A 答 的人把顺序查找的次数错安过来;C C 答 的人以为一步命中;D D 答 的人低估了收缩轮数—— 缩到 至少要四步中的三步外加命中一步。
01int a[] = {1, 3, 3, 5, 7, 7, 7, 9}; 02int x = 7; 03int l = 0, r = 8; 04while (l < r) { 05 int mid = (l + r) / 2; 06 if (a[mid] >= x) r = mid; 07 else l = mid + 1; 08} 09cout << l << " " << a[l];
输出是( )。
考点:手写 lower_bound(J6)。
(J6)考点:手写 lower_bound——第一个大于等于 7 的位置是 4。
解析:本题考查手写 lower_bound。a[mid] >= x 为真就退右界(答案在 mid 或更左),假就推左界;收敛在 ,值 ,输出 4 7。三个连续 中它精准停在最前面那个。
排除法:C C 答 6 7 的人停到了重复段的中间或末尾,等号方向没扣准;B B 答 7 9 的人做成了 upper_bound;D D 答 3 5 的人收缩方向反了停进了左侧。
01int a[] = {1, 3, 3, 5, 7, 7, 7, 9}; 02int x = 7; 03int l = 0, r = 8; 04while (l < r) { 05 int mid = (l + r) / 2; 06 if (a[mid] > x) r = mid; 07 else l = mid + 1; 08} 09cout << l << " " << a[l];
输出是( )。
考点:手写 upper_bound(J7)。
(J7)考点:手写 upper_bound——第一个大于 7 的位置是 7。
解析:本题考查手写 upper_bound。a[mid] > x 为真退右界,假(含等于)推左界;连续的 全被「等于也推左」跳过,收敛在下标 (值 ),输出 7 9。与 J6 一对比,「差一个等号」的语义立现。
排除法:B B 答 6 7 的人停在最后一个 ,把 upper 当成了「最后一个等于」;C C 答 4 7 的人做成了 lower_bound;A A 答 8 9 的人越过了第一个大于位置, 初值 本身不可达。
01int a[] = {2, 4, 4, 4, 6, 8}; 02int x = 4; 03int l = 0, r = 5; 04while (l < r) { 05 int mid = (l + r) / 2; 06 if (a[mid] >= x) r = mid; 07 else l = mid + 1; 08} 09if (a[l] == x) cout << l; 10else cout << -1;
输出是( )。
考点:第一次出现位置(K1)。
(K1)考点:第一次出现位置——lower_bound 收敛后再判等,得首个等于的下标。
解析:本题考查第一次出现位置。a[mid] >= 4 的收缩收敛在下标 ——第一个不小于 处,恰是首个 ;末尾 a[l] == x 再确认存在性,存在输出 ,否则 。
排除法:B B 答 的人停到了重复段末尾,等号方向没扣准;C C 答 的人收缩方向反了;A A 答 的人没看到数组里明明有 。
01int a[] = {2, 4, 4, 4, 6, 8}; 02int x = 4; 03int l = 0, r = 6; 04while (l < r) { 05 int mid = (l + r) / 2; 06 if (a[mid] <= x) l = mid + 1; 07 else r = mid; 08} 09cout << l - 1;
输出是( )。
考点:最后一次出现位置(K2)。
(K2)考点:最后一次出现位置——先找第一个大于 x 的位置,减一即最后一个等于。
解析:本题考查最后一次出现位置。a[mid] <= 4 推左界的写法收敛在「第一个大于 」的位置 (值 ), 正是最后一个 。「upper 减一」与「上取整中点」(K6)是同一目标的两种路线。
排除法:B B 答 的人给成了第一个 ,把边界目标弄反;A A 答 的人忘了减一,停在了大于处;D D 答 的人停在重复段中间。
01int a[] = {1, 3, 5, 7, 9}; 02int x = 6; 03int l = 0, r = 5; 04while (l < r) { 05 int mid = (l + r) / 2; 06 if (a[mid] >= x) r = mid; 07 else l = mid + 1; 08} 09cout << l;
输出是( )。
考点:插入位置(K3)。
(K3)考点:插入位置——第一个大于等于 6 的位置即保序插入点。
解析:本题考查插入位置。>= 6 的收缩停在下标 (值 ), 插在它前面,左侧 全小、右侧 全大,有序保持——插入点语义与 lower_bound 重合(D7)。
排除法:C C 答 的人把 插到 前面打乱了序;A A 答 的人把插入点挪到 后面同样乱序;D D 答 的人把元素的值当下标。
01long long n = 40; 02long long l = 0, r = 40, ans = 0; 03while (l <= r) { 04 long long mid = (l + r) / 2; 05 if (mid * mid <= n) { ans = mid; l = mid + 1; } 06 else r = mid - 1; 07} 08cout << ans;
输出是( )。
考点:整数平方根(K4)。
(K4)考点:整数平方根——值域上二分找最大的 mid 使平方不超过 n。
解析:本题考查整数平方根。mid*mid <= 40 在 处真、 处假,ans 最终记下 ()。这是二分答案骨架在「值域」上的最小样例,变量用 long long 防 mid*mid 溢出(E6)。
排除法:C C 答 的人把「四舍五入到最接近的平方根」当成了目标,二分找的是整数下取整根;B B 答 的人少试探了一步;D D 答 的人把 的平方当成了答案输出。
l = 2000000000、r = 2100000000 时写 mid = (l + r) / 2,风险是( )。
考点:中点溢出的风险(K5)。
(K5)考点:中点溢出的风险——两数之和超 int 上限变负,中点跟着错。
解析:本题考查中点溢出的风险。,超出约 的 int 上限,加法结果回绕为负,除以 后 mid 仍是负数,用作下标立即越界。防御写法 l+(r-l)/2(C1)。
排除法:A A 以为 mid 变 的人没推过回绕后的数值;D D 以为自动升级 long long 的人高估了编译器——int+int 就是 int;B B 以为 int 存任意大数的人不了解定长整型的硬边界。
01int a[] = {1, 2, 2, 2, 5}; 02int x = 2; 03int l = 0, r = 4; 04while (l < r) { 05 int mid = (l + r + 1) / 2; 06 if (a[mid] <= x) l = mid; 07 else r = mid - 1; 08} 09cout << l << " " << a[l];
输出是( )。
考点:上取整中点执行(K6)。
(K6)考点:上取整中点执行——加一取中配 l = mid,安全找到最后一个 2。
解析:本题考查上取整中点执行。找最后一个 :区间 中点 (值 )l=2;区间 中点 (值 )l=3;区间 中点 (值 )r=3,收敛输出 3 2。加一让两元素区间时中点落在右侧,l=mid 也能推进。
排除法:B B 答 1 2 的人给的是第一个 ,目标弄反;A A 答 4 5 的人越过了 这个不满足的元素;C C 答 2 2 的人停在了重复段中间,没压到最右。
01int a[] = {20, 15, 10, 17}; 02int cut = 15; 03int total = 0; 04for (int i = 0; i < 4; i++) 05 if (a[i] > cut) total += a[i] - cut; 06cout << total;
输出是( )。
考点:砍树截取统计(L1)。
(L1)考点:砍树截取统计——check 内一次线性求和:高于 cut 的部分累加。
解析:本题考查砍树截取统计。高度定 : 截得 、 截得 , 与 不高于 无贡献,共 。这是二分答案的 check 原型:给定候选值, 判定。
排除法:B B 答 的人把四棵树全高度加了起来,忘了只有高出部分;A A 答 的人漏了 那棵的 ;D D 答 的人以为不高于最高树就没得截, 明明有截。
01int a[] = {20, 15, 10, 17}; 02int need = 7; 03int l = 0, r = 20, ans = 0; 04while (l <= r) { 05 int mid = (l + r) / 2; 06 int total = 0; 07 for (int i = 0; i < 4; i++) 08 if (a[i] > mid) total += a[i] - mid; 09 if (total >= need) { ans = mid; l = mid + 1; } 10 else r = mid - 1; 11} 12cout << ans;
输出是( )。
考点:砍树二分主体(L2)。
(L2)考点:砍树二分主体——ans 记录法在值域 0 到 20 上锁出最大可行高度 15。
解析:本题考查砍树二分主体。总需求 :高度 时截得 可行,记 ans=15 继续上探;高度 时只截得 不可行回退,最终 ans 停在 。二分轮数仅 轮,每轮一次 check。
排除法:C C 答 的人把可行边界向低错移一格, 可行但不是最大;B B 答 的人没算清 只截得 ;A A 答 的人把需求量当成了答案。
石头距起点依次为 、、、、,终点在 ,最多移走 块石头。判定「最短跳跃距离能否达到 」:从起点向终点扫,与上一块保留位置距离小于 的石头就移走。需要的移走数是( )。
考点:跳石头判定(L3)。
(L3)考点:跳石头判定——mid=4 时贪心扫描只需移走 2 块,可行。
解析:本题考查跳石头判定。距离序列(含起点 ):。判定 :首距 移走第一块();跨到 后, 距 仅 再移(); 距 为 保留,、终点各距 恰好达标。移走 ,可行。
排除法:C C 答 块不可行的人把 mid=5 的判定结果带了过来—— 确实要移 块,但本题判定的是 ;A A 答 块的人漏移了 ;B B 答 块的人把全部石头都算了进去,贪心能不移就不移。
两块巧克力尺寸分别为 与 ,按边长 切正方形,能切出的总份数是( )。
考点:分巧克力统计(L4)。
(L4)考点:分巧克力统计——每块按整除下取整切分求和。
解析:本题考查分巧克力统计。边长 : 切出 块, 切出 块,共 份。整除向下取整保证每份都是完整的 。
排除法:D D 答 份的人把面积除以 再四舍五入, 的零头 切不成正方形;C C 答 份的人漏了第二块;A A 答 的人直接拿面积当了份数。
01int l = 0, r = 30, ans = -1; 02while (l <= r) { 03 int mid = (l + r) / 2; 04 if (mid * mid <= 30) { ans = mid; l = mid + 1; } 05 else r = mid - 1; 06} 07cout << ans;
输出是( )。
考点:单调判定二分执行(L5)。
(L5)考点:单调判定二分执行——值域 0 到 30 上找最大平方不超过 30 的数。
解析:本题考查单调判定二分执行。mid*mid <= 30 真假分界在 :可行即记录并右推,不可行回退,ans 终值 ()。与 K4 同骨架不同数据,巩固「记录法」套路。
排除法:D D 答 的人越过了分界, 已不可行;C C 答 的人把值域右端当成了答案;A A 答 的人输出的是 的平方。
01int l = 0, r = 100; 02while (l < r) { 03 int mid = (l + r) / 2; 04 if (mid >= 17) r = mid; 05 else l = mid + 1; 06} 07cout << l;
输出是( )。
考点:最小化答案框架(L6)。
(L6)考点:最小化答案框架——l<r 写法求第一个满足条件的位置。
解析:本题考查最小化答案框架。mid >= 17 为真退右界(答案在 mid 或更左),假推左界; 收敛到分界 ,输出 。「l < r + r = mid」是无死循环风险的最小化模板,与闭区间写法(E3)两套骨架并存。
排除法:B B 答 的人把分界向低错移, 不成立;C C 答 的人把「第一个满足」当成了「分界之后」;A A 答 的人输出成了区间中点。
01double x = 2.0; 02double l = 0, r = 2.0; 03for (int i = 0; i < 3; i++) { 04 double mid = (l + r) / 2; 05 if (mid * mid <= x) l = mid; 06 else r = mid; 07} 08cout << l << " " << r;
输出是( )。
考点:实数平方根区间(M1)。
(M1)考点:实数平方根区间——三轮二分后区间收缩为 [1.25, 1.5]。
解析:本题考查实数平方根区间。求 :首轮中点 , 左界推到 ;次轮中点 , 右界退到 ;三轮中点 , 左界推到 。根约 被夹在 1.25 1.5 之间,宽度从 缩到 。
排除法:A A 答 1 1.5 的人少推了第三轮的左界;C C 答 1.5 2 的人首轮方向做反—— 的平方不超过 该推左界;D D 答 1.414 1.415 的人给出的是几十轮后的精度,三轮远远到不了。
浮点二分初始区间宽度为 ,要求把区间收缩到宽度小于 ,至少需要二分约( )次。
考点:精度与次数估算(M2)。
(M2)考点:精度与次数估算——宽度 100 收缩到 1e-6 约需 27 次。
解析:本题考查精度与次数估算。每轮宽度减半, 即 ,而 ,故 次。估算功底:, 即约 。
排除法:A A 答 的人只算到 ,离 差六个数量级;D D 答 的人以为次数与初始宽度同阶,对数收缩用不了这么多;B B 答 的人连 都除不到。
01double l = 1, r = 2; // f(t) = t^3 - 3,两端函数值异号 02for (int i = 0; i < 4; i++) { 03 double mid = (l + r) / 2; 04 if (mid * mid * mid - 3 < 0) l = mid; 05 else r = mid; 06} 07cout << l << " " << r;
输出是( )。
考点:方程根二分(M3)。
(M3)考点:方程根二分——按中点符号收缩,四轮后根夹在 1.4375 与 1.5 之间。
解析:本题考查方程根二分。:、 异号;中点 处 右界退到 ;中点 处 左界推到 ;中点 处 左界推到 ;中点 处约 左界推到 。真根约 被夹在 1.4375 1.5 内。
排除法:C C 答 1.25 1.5 的人少算了两轮左推;B B 答 1.5 1.75 的人首轮方向做反—— 已超过 ,应把右界退到 ;A A 答 1.375 1.5 的人漏了第四轮。
补全下面二分查找的循环条件:
01int a[] = {2, 5, 8, 12, 16, 23, 38, 56}; 02int x = 16; 03int l = 0, r = 7; 04int res = -1; 05while (/* 1 */) { 06 int mid = (l + r) / 2; 07 if (a[mid] == x) { res = mid; break; } 08 else if (a[mid] < x) l = mid + 1; 09 else r = mid - 1; 10}
空位 /* 1 */ 处应填( )。
考点:补全循环条件(N1)。
(N1)考点:补全循环条件——闭区间二分循环条件是 l <= r。
解析:本题考查补全循环条件。闭区间 [l, r] 非空即 l <= r,区间空才是终止条件;填 l < r 会在 l == r 时漏判最后一个候选(C2)。补全后找 :中点 (值 )推左界,中点 (值 )退右界,中点 命中。
排除法:B B 填 l < r 的人漏掉单元素区间的最后一查;A A 填 l != r 在特定收缩下与 l < r 同病;C C 填 r - l > 1 提前一轮退出,两元素区间没查完就停。
补全下面二分的中点计算:
01int a[] = {2, 5, 8, 12, 16, 23, 38, 56}; 02int x = 8; 03int l = 0, r = 7; 04int res = -1; 05while (l <= r) { 06 int mid = /* 1 */; 07 if (a[mid] == x) { res = mid; break; } 08 else if (a[mid] < x) l = mid + 1; 09 else r = mid - 1; 10}
空位 /* 1 */ 处应填( )。
考点:补全中点计算(N2)。
(N2)考点:补全中点计算——中点是下标区间的中点 (l + r) / 2。
解析:本题考查补全中点计算。中点由左右界下标相加折半;l + r 不折半会越界,(r - l) / 2 少了基准 l 的偏移,a[l] + a[r] 混了值域与下标域(O5)。补全后找 :中点 ()退右界到 ,中点 ()推左界到 ,中点 命中。
排除法:C C 填 l + r 的人直接把和当成了中点,十有八九越界;D D 填 (r - l) / 2 的人忘了加回 l,中点永远偏左;B B 填 a[l] + a[r] 的人把元素值当下标用。
补全下面二分查找的区间收缩:
01int a[] = {2, 5, 8, 12, 16, 23, 38, 56}; 02int x = 23; 03int l = 0, r = 7; 04int res = -1; 05while (l <= r) { 06 int mid = (l + r) / 2; 07 if (a[mid] == x) { res = mid; break; } 08 else if (a[mid] < x) /* 1 */ ; 09 else r = mid - 1; 10}
空位 /* 1 */ 处应填( )。
考点:补全区间收缩(N3)。
(N3)考点:补全区间收缩——偏小时 l = mid + 1 向右半收缩。
解析:本题考查补全区间收缩。a[mid] < x 说明目标在右侧且 mid 已排除,l = mid + 1;填 l = mid 把比过的 mid 留在区间,r = ... 两个选项则背向目标方向收缩(B3、P3)。补全后找 :中点 ()推左界到 ,中点 ()命中。
排除法:A 填 l = mid 的人给死循环埋雷,两元素区间时原地踏步;D、C 填 r = mid + 1、r = mid - 1 的人方向整个反了,把目标所在的右半丢弃。
「求最大可行答案」的二分答案中,补全可行分支的动作:
01int l = 0, r = 1000000, ans = 0; 02while (l <= r) { 03 int mid = (l + r) / 2; 04 if (check(mid)) { /* 1 */ ; l = mid + 1; } 05 else r = mid - 1; 06} 07cout << ans;
空位 /* 1 */ 处应填( )。
考点:补全答案记录(N4)。
(N4)考点:补全答案记录——可行分支记 ans = mid 再右推。
解析:本题考查补全答案记录。求最大可行答案的骨架:可行即 ans = mid; l = mid + 1,不可行 r = mid - 1;ans 只在可行时被刷新,终值即最大可行值(F5、L2 实测)。
排除法:B B 填 ans = l 的人记录了错误的量——此刻 l 尚未更新、与 mid 无必然关系;C C 填 mid = ans 的人把赋值方向写反,破坏了 mid;D D 填 break 的人拿到首个可行值就停手,那通常远不是最大。
砍树问题的 check(cut) 统计截得的木材总量,补全累计式:
01int h[] = {20, 15, 10, 17}; 02int n = 4, cut = 15; 03int total = 0; 04for (int i = 0; i < n; i++) 05 if (h[i] > cut) total += /* 1 */ ;
空位 /* 1 */ 处应填( )。
考点:补全截取统计(N5)。
(N5)考点:补全截取统计——高出砍伐线的部分是 h[i] - cut。
解析:本题考查补全截取统计。每棵树贡献「自身高度减砍伐线」(高出的部分才截得):、,合计 (L1 实测)。截不到的树被 if (h[i] > cut) 拦在外面。
排除法:D D 填 cut - h[i] 的人把差值方向写反,矮树会贡献负数;A A 填 h[i] 的人把整棵树都算了进去;C C 填 h[i] + cut 的人把减法做成了加法,统计量纲全错。
补全浮点二分求平方根的循环条件:
01double x = 2.0; 02double l = 0, r = 2.0; 03while (/* 1 */) { 04 double mid = (l + r) / 2; 05 if (mid * mid <= x) l = mid; 06 else r = mid; 07} 08cout << (l + r) / 2;
空位 /* 1 */ 处应填( )。
考点:补全浮点终止(N6)。
(N6)考点:补全浮点终止——r - l > 1e-6 即宽度仍大于精度,继续收缩。
解析:本题考查补全浮点终止。浮点二分以区间宽度为终止依据:宽度大于 eps 说明近似程度不够,循环继续;停止时两端都在答案精度邻域内(G2)。填反成 < 一轮都不进,填 mid != x 永远等不到浮点恰好相等。
排除法:D D 填 r - l < 1e-6 的人把「继续条件」与「终止条件」弄反,区间还没收缩就退出;B B 填 l < r - 1 的人把整数尺度套到浮点上, 的宽度远不够精度;A A 填 mid != x 的人指望浮点恰好相等,二分到宇宙热寂也等不到。
01int a[] = {5, 1, 9, 3, 7}; 02sort(a, a + 5); 03int x = 7; 04int l = 0, r = 4, res = -1; 05while (l <= r) { 06 int mid = (l + r) / 2; 07 if (a[mid] == x) { res = mid; break; } 08 else if (a[mid] < x) l = mid + 1; 09 else r = mid - 1; 10} 11cout << res;
输出是( )。
考点:排序后多次二分(O1)。
(O1)考点:排序后多次二分——无序数据先 sort 再二分,下标是排好序后的新位置。
解析:本题考查排序后多次二分。{5,1,9,3,7} 排序后是 {1,3,5,7,9}, 的新下标是 ,二分三轮内命中。要点:返回的下标属于排序后的数组,不是原始位置;数据只排一次,后面成千上万次查询每次 (H4)。
排除法:A A 答 的人以为 还在原来的第 个位置;B B 答 的人误以为无序数组二分必失败,但代码里排了序;D D 答 的人输出成了元素值。
01int a[] = {1, 2, 2, 2, 2, 5, 6}; 02int x = 2; 03int lo = 0, hi = 7; 04while (lo < hi) { 05 int mid = (lo + hi) / 2; 06 if (a[mid] >= x) hi = mid; 07 else lo = mid + 1; 08} 09int up = lo; 10lo = 0; hi = 7; 11while (lo < hi) { 12 int mid = (lo + hi) / 2; 13 if (a[mid] > x) hi = mid; 14 else lo = mid + 1; 15} 16cout << up << " " << lo << " " << (lo - up);
输出是( )。
考点:二分统计次数执行(O2)。
(O2)考点:二分统计次数执行——lower 停在下标 1、upper 停在下标 5,差值 4 即出现次数。
解析:本题考查二分统计次数执行。第一段 a[mid] >= 2 收敛在 (首个 ),第二段 a[mid] > 2 收敛在 (首个 ),中间下标 至 恰是四个 ,输出 1 5 4。两次边界二分组合出 O(log n) 的计数,胜过顺序扫描的 O(n)。
排除法:C C 答 1 5 3 的人把「区间内元素个数」错算成了差值减一,半开区间左闭右开长度恰为差;B B 答 0 4 4 的人两次边界都向左偏一格;A A 答 2 5 3 的人把 lower 停到了重复段中间。
01int a[] = {2, 5, 8, 12, 16}; 02int x = 13; 03int l = 0, r = 4; 04while (l < r) { 05 int mid = (l + r) / 2; 06 if (a[mid] < x) l = mid + 1; 07 else r = mid; 08} 09if (l > 0 && x - a[l - 1] <= a[l] - x) cout << a[l - 1]; 10else cout << a[l];
输出是( )。
考点:查找最接近的数(O3)。
(O3)考点:查找最接近的数——定位插入点后与左右邻居比距离。
解析:本题考查查找最接近的数。a[mid] < 13 推左界的收缩停在第一个 的位置 (值 );候选只剩左邻 (差 )与自身 (差 ),取较近的 输出。不存在的目标也能顺带找到最近邻,这是边界二分的实用延伸。
排除法:B B 答 的人只看了右侧邻居,忘了比一比左邻;A A 答 的人输出了不存在的目标本身;C C 答 的人把次近的元素当成了最近, 明明更近。
01int a[] = {0, 1, 3, 4, 5}; // 本应是从 0 开始的连续数,缺了一个 02int l = 0, r = 4, ans = 5; 03while (l <= r) { 04 int mid = (l + r) / 2; 05 if (a[mid] == mid) l = mid + 1; 06 else { ans = mid; r = mid - 1; } 07} 08cout << ans;
输出是( )。
考点:二分找缺失数(O4)。
(O4)考点:二分找缺失数——a[i] == i 的真假分界即缺失位置。
解析:本题考查二分找缺失数。从 开始的连续数缺一个:缺失位置之前 a[i] == i 恒成立,之后 a[i] == i+1 恒成立,真假分界即答案。追踪:中点 (值 )记 ans=2 退右界到 ;中点 ()推左界;中点 ()推左界,交错退出输出 。
排除法:C C 答 的人把「第一个错位处的值」当成了答案,要的是「错位处的下标/应有值」;D D 答 的人输出了哨兵初值,说明追踪中 ans 未被更新过——实际第二轮就更新了;B B 答 的人没做追踪凭直觉选了开头。
二分查找代码中,l、r、mid 的身份是( )。
考点:下标与值的辨析(O5)。
(O5)考点:下标与值的辨析——l、r、mid 是下标,a[mid] 才是值。
解析:本题考查下标与值的辨析。移动的是下标区间 [l, r],比较的是元素值 a[mid] 与 x;输出时 mid 是位置、a[mid] 是元素,二者在题里反复互换,分不清就会答错(J1 与 O1 的干扰项专门考此)。
排除法:C C 以为全是值的人没注意循环变量从不出现在 a[] 外;A A 以为 l、r 是值 mid 是下标的人自相矛盾;B B 以为存地址的人把数组访问与指针混为一谈。
「找最后一个满足条件的位置」的二分中,写了 l = mid 但中点仍是 (l + r) / 2,当区间收缩到只剩两个元素 [l, l+1] 且 a[mid] <= x 时会发生的现象是( )。
考点:死循环的成因(P1)。
(P1)考点:死循环的成因——l = mid 配下取整中点,两元素区间时原地踏步。
解析:本题考查死循环的成因。区间剩 [l, l+1] 时 (l+r)/2 = l,若此刻判定为真执行 l = mid,区间纹丝不动,下一轮原样重演,永不终止。药方:l = mid 必须配 (l+r+1)/2(D5、K6)。这是二分最经典的坑,没有报错、没有异常,程序安静地挂死。
排除法:C C 以为正常结束的人没在纸上推过两元素区间;D D 以为编译器报错的人把运行期逻辑错误当成了编译期检查;B B 以为越界访问的人混淆了两种典型故障——越界来自 mid 为负或超界,死循环来自区间不缩。
l 和 r 都是接近 int 上限的大数时,(l + r) / 2 出错,避免这个问题的正确写法是( )。
考点:中点加法溢出(P2)。
(P2)考点:中点加法溢出——l + (r - l) / 2 先减后加不越界。
解析:本题考查中点加法溢出。两数之和先于除法计算,超过 int 上限即回绕;先做 r - l 差值很小,加回 l 也不越界(C1、K5 展示了回绕后果)。
排除法:B B 填 % 2 的人得到的是奇偶标志不是中点;A A 填 r - l / 2 的人运算优先级理解错,等价于 r - (l/2);D D 选转浮点的人以为换类型就高枕无忧——精度丢失在大数区间同样埋雷,先减后加才是正解。
升序数组二分查找中,把 a[mid] < x 的分支误写成 r = mid - 1,可能的后果是( )。
考点:收缩方向写反(P3)。
(P3)考点:收缩方向写反——背弃目标方向收缩,误报不存在。
解析:本题考查收缩方向写反。升序中 a[mid] < x 明示目标在右侧,若反而把右界收缩,目标所在区间被整段丢弃;循环会正常终止(区间照样缩小),最终带着初值 「若无其事」地返回失败——编译运行全无异样,只有结果错。
排除法:A A 以为一定崩溃的人高估了故障烈度,越界访问才可能崩,这里只是安静地错;B B 以为一定死循环的人把两类故障混为一谈,区间在缩小就不会死循环;C C 以为没有影响的人大概没跑过这版代码。
对无序数组直接使用标准二分查找,结果是( )。
考点:无序数组二分(P4)。
(P4)考点:无序数组二分——排除一半失去依据,结果不可信。
解析:本题考查无序数组二分。有序性是「比较一次淘汰一半」的合法性来源;无序时 a[mid] < x 不能说明目标在右,所谓收缩是在乱砍,可能把真实目标所在的区间砍掉。注意它不是「一定找不到」——运气好也可能碰上,恰恰这种时对时错最危险。
排除法:A A 以为一定找不到的人把「不可信」绝对化了;B B 以为自动排序的人给语言虚构了不存在的能力;D D 以为等价顺序查找的人没注意二分只看了 个元素,远没有顺序查找扫得全。
「求最大可行答案」的二分答案中,check(mid) 为真时误写成了 r = mid - 1,后果是( )。
考点:check 方向写反(P5)。
(P5)考点:check 方向写反——可行时向劣收缩,收敛到下界附近。
解析:本题考查 check 方向写反。求最大值却在可行时执行 r = mid - 1,等于「越可行越往小退」,最终答案挤到可行段的最小端甚至停在初值;逻辑自洽、程序不崩,只是答非所问。对照 E3 的正确骨架可立即定位。
排除法:A A 以为一定死循环的人又混淆了故障类型,区间照常缩小不会死循环;C C 以为输出不变的人低估了方向的力量——方向反了答案完全不同;D D 以为编译不过的人不了解这是纯逻辑错误。
二分循环结束后,直接使用循环内的 mid 变量(如输出 a[mid])的问题是( )。
考点:循环外误用中点(P6)。
(P6)考点:循环外误用中点——mid 只在循环体内有意义,结果要用 res 或 l。
解析:本题考查循环外误用中点。循环结束时 mid 保存的是最后一次试探的位置:成功时它恰是命中处,失败时它停在交错前的最后试探,与「答案」没有任何保证关系;规范做法是读 res(命中记录)或 l(边界二分收敛点)。
排除法:A A 以为循环外 mid 自动清零输出 的人给变量虚构了不存在的复位;D D 以为编译错误的人不知道循环变量在循环外照样可读;C C 以为 mid 就是答案的人在失败路径上会拿到完全无关的下标。