第7类题型:二分
为什么二分题你明明背过模板,一到面试还是容易写出死循环
很多同学第一次刷二分,会觉得它比链表、回溯简单,因为代码看起来就十几行。但真正到面试现场,二分反而特别容易暴露基本功问题。原因不是你不会比较大小,而是你没有真正固定自己的区间定义。
二分题最常见的翻车方式,通常就这几种:
- 你背过闭区间和左闭右开两个版本,结果写题时混用了。
- 你知道要算
mid,但边界更新时没有把mid排除出去,直接把循环写成死循环。 - 你能做"找到目标值",一到"找左边界、右边界、插入位置"就开始发虚。
如果你现在处在"模板大概会背,但每次都怕边界写错"的阶段,这一类题一定要单独练透。读完这篇,你至少要把 3 件事彻底练熟:先判断题目有没有单调性、先固定一套区间模板、能在面试里解释为什么当前这次更新不会漏答案。
文章目录
- 第7类题型:二分
-
- 为什么二分题你明明背过模板,一到面试还是容易写出死循环
- [1. 核心知识点](#1. 核心知识点)
- [2. 这类题在面试里考什么](#2. 这类题在面试里考什么)
- [3. 高频题清单](#3. 高频题清单)
- [4. 这类题最容易犯的 3 个错误](#4. 这类题最容易犯的 3 个错误)
- [5. 代表题精讲 1](#5. 代表题精讲 1)
-
- 题目
- 思路
- [Java 代码](#Java 代码)
- 复杂度
- 边界提醒
- 如果这是面试现场,你可以这样说
- [6. 代表题精讲 2](#6. 代表题精讲 2)
-
- 题目
- 思路
- [Java 代码](#Java 代码)
- 复杂度
- 边界提醒
- 如果这是面试现场,你可以这样说
- [7. 其余题模板与关键片段](#7. 其余题模板与关键片段)
-
- [\`35. 搜索插入位置\`(https://leetcode.cn/problems/search-insert-position/)](#
35. 搜索插入位置)
- [\`35. 搜索插入位置\`(https://leetcode.cn/problems/search-insert-position/)](#
- [8. 二分类题先判断什么](#8. 二分类题先判断什么)
- [9. 错题本记录方式](#9. 错题本记录方式)
- [10. 面试前 3 分钟速记](#10. 面试前 3 分钟速记)
- [11. 最后总结:二分真正难的不是模板,而是边界纪律](#11. 最后总结:二分真正难的不是模板,而是边界纪律)
1. 核心知识点
二分最核心的不是"找中点",而是"在有序或满足单调性的范围里,每次排除掉一半不可能的答案"。
这篇统一使用闭区间 [left, right]。只要你选了这套定义,后面的循环条件和边界更新就必须跟着一起固定:
java
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
这里有 3 个关键点:
left <= right:因为闭区间里,left == right时仍然可能有答案。mid = left + (right - left) / 2:避免溢出,也更稳。- 更新边界时必须写成
mid + 1或mid - 1:因为mid已经比较过,不能继续留在待搜索区间里。
动画辅助理解:二分真正缩掉的是什么
你可以先打开这页分步动画:

这个动画最值得盯住的是 4 步:
- 先说清楚区间到底是闭区间
[left, right],还是左闭右开[left, right)。 - 再稳定算出
mid,保证比较位置明确。 - 根据
nums[mid]和target的关系,丢掉确定不可能的一半。 - 继续收缩,直到区间完全收敛。
拿数组 nums = [1, 3, 5, 7, 9]、target = 8 举例:
| 轮次 | left | right | mid | numsmid | 结论 |
|---|---|---|---|---|---|
| 第 1 轮 | 0 | 4 | 2 | 5 | 5 < 8,左半边和 mid 都不可能,left = 3 |
| 第 2 轮 | 3 | 4 | 3 | 7 | 7 < 8,继续右移,left = 4 |
| 第 3 轮 | 4 | 4 | 4 | 9 | 9 > 8,排除右侧,right = 3 |
| 结束 | 4 | 3 | - | - | left > right,说明没找到,插入位置就是 left = 4 |
这张表最重要的不是结果,而是你要看清楚:每一轮都必须让区间真实变小。只要某一轮比较完以后,还把 mid 留在区间里,二分就容易卡住。
2. 这类题在面试里考什么
二分题真正考的不是模板记忆,而是你能不能在极少的代码里把边界处理得非常干净。
面试官通常会看:
- 你能不能先判断题目是否具备单调性或有序性。
- 你是否固定了一套区间定义,而不是边写边变。
- 你会不会把
mid的更新写错,导致漏答案或死循环。 - 你能不能从"找目标值"平滑扩展到"找边界"和"找插入位置"。
真正的区分度在于:你不只是把代码写出来,还能解释为什么当前边界更新一定不会漏掉正确答案。
3. 高频题清单
| 题目 | 来源 | 难度 | 高频属性 |
|---|---|---|---|
704. 二分查找 |
LeetCode 热题 100 | Easy | 面试高频 |
35. 搜索插入位置 |
面试经典 150 | Easy | 高频 |
34. 在排序数组中查找元素的第一个和最后一个位置 |
LeetCode 热题 100 | Medium | 高频 Medium |
4. 这类题最容易犯的 3 个错误
- 区间定义没固定,闭区间和左闭右开混着写。
- 边界更新没有排除
mid,导致区间不收缩。 - 只会标准二分,不会把模板改造成左边界、右边界或插入位置。
5. 代表题精讲 1
题目
思路
这是最标准的模板题,核心不是"写出来",而是"每一步都能解释清楚为什么这么写"。
流程非常稳定:
- 用
left和right表示当前候选区间。 - 取中点
mid进行比较。 - 命中就直接返回。
- 如果
nums[mid] < target,说明目标只可能在右边,left = mid + 1。 - 如果
nums[mid] > target,说明目标只可能在左边,right = mid - 1。
Java 代码
java
class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
}
复杂度
- 时间复杂度:
O(log n) - 空间复杂度:
O(1)
边界提醒
这题最容易出错的地方有两个:
- 循环条件必须和区间定义配套。既然用的是闭区间
[left, right],就必须写left <= right。 - 更新边界时不能写成
left = mid或right = mid。因为mid已经比较过了,继续保留会导致区间缩不动。
你可以自己代一个最容易挂的样例:nums = [5],target = 2。如果你写成 right = mid,那第一轮 mid 就还是 0,区间不会收缩,循环就容易卡死。
如果这是面试现场,你可以这样说
这题是标准二分模板题,前提是数组有序。我这里统一使用闭区间 [left, right],所以循环条件写 left <= right。每一轮比较 nums[mid] 和 target 后,都能明确排除一半区间;而且边界更新必须写成 mid + 1 或 mid - 1,保证当前比较位置不会被重复保留在区间里。
6. 代表题精讲 2
题目
思路
这题最重要的认知是:它不是"一次二分找两个答案",而是"两次二分分别找左右边界"。
为什么要拆成两次:
- 找左边界时,即使命中了
target,也不能立刻停,因为前面可能还有更靠左的目标值。 - 找右边界时,同理,也要继续往右压缩,看看后面还有没有目标值。
所以逻辑会变成:
- 找左边界:只要
nums[mid] >= target,就往左收缩。 - 找右边界:只要
nums[mid] <= target,就往右收缩。
Java 代码
java
class Solution {
public int[] searchRange(int[] nums, int target) {
int left = findLeft(nums, target);
int right = findRight(nums, target);
return new int[] {left, right};
}
private int findLeft(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
int answer = -1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] >= target) {
if (nums[mid] == target) {
answer = mid;
}
right = mid - 1;
} else {
left = mid + 1;
}
}
return answer;
}
private int findRight(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
int answer = -1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] <= target) {
if (nums[mid] == target) {
answer = mid;
}
left = mid + 1;
} else {
right = mid - 1;
}
}
return answer;
}
}
复杂度
- 时间复杂度:
O(log n) - 空间复杂度:
O(1)
边界提醒
这题最容易错的,不是两次二分本身,而是"命中 target 之后到底停不停"。
比如 nums = [5, 7, 7, 8, 8, 10],target = 8:
- 找左边界时,如果你在第一次命中
8时就直接返回,很可能拿到的是下标4,不是最左边的3。 - 找右边界时,如果命中后不继续向右试探,就可能只能拿到中间某个
8,而不是最后一个位置。
因此这题要记住一句话:命中目标值不代表结束,边界题要看"命中后是否还要继续收缩"。
如果这是面试现场,你可以这样说
这题我会拆成两次二分。第一次专门找最左出现位置,所以在 nums[mid] >= target 时继续往左压缩;第二次专门找最右出现位置,所以在 nums[mid] <= target 时继续往右压缩。关键不在于"有没有找到 target",而在于"命中之后是否还要继续寻找更靠左或更靠右的答案"。
7. 其余题模板与关键片段
35. 搜索插入位置
这题和 704 的模板几乎一样,只是如果最后没找到,返回值不是 -1,而是 left:
java
class Solution {
public int searchInsert(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return left;
}
}
为什么最后返回 left:
- 如果目标值存在,前面已经返回了。
- 如果不存在,循环结束时
left正好停在"第一个大于等于 target 的位置"。 - 如果数组里所有数都比
target小,那left最后会走到nums.length,这正好也是尾部插入的位置。
8. 二分类题先判断什么
真正动手写之前,你可以先问自己 4 个问题:
- 这题是不是建立在有序数组或单调性之上。
- 我这次统一用闭区间,还是左闭右开。
- 我当前是在找"某个值",还是在找"某个边界"。
- 命中目标值之后,我是可以返回,还是还要继续收缩区间。
这 4 个问题一旦提前问清楚,二分题大部分边界错误都能少掉一半。
9. 错题本记录方式
二分题建议重点记录:
- 这题的单调性到底是什么。
- 我使用的是哪套区间定义。
- 哪一轮边界更新没有把区间真正缩小。
- 这题是在找目标值、插入位置,还是左右边界。
10. 面试前 3 分钟速记
- 二分先看"有序 / 单调"。
- 先固定一套区间定义,不要混写。
mid = left + (right - left) / 2。- 更新边界时必须排除已经比较过的
mid。 - 找边界题时,关键不是"命中没命中",而是"命中后还要不要继续缩"。
11. 最后总结:二分真正难的不是模板,而是边界纪律
如果你只能记住一句话,那就是:二分题最怕的不是不会写,而是边界定义不统一,导致你自己也不知道当前区间里还剩谁。
面试时你只要按这个顺序去讲,思路就会非常稳:
- 先说明这题为什么能二分,是有序还是有单调性。
- 再说明你选的是哪套区间定义。
- 接着解释当前比较后,为什么能排除左边或右边。
- 如果是边界题,再补一句"命中后为什么还不能立刻返回"。
这样你就不只是会背一个模板,而是真的掌握了二分这类题的公共骨架。
上上期速递:【Java实习面试算法冲刺】栈、队列与单调栈
上期速递:【Java实习面试算法冲刺】二叉树
感谢阅读,记得点赞、关注、收藏,欢迎各位评论区交流!!!