【Java实习面试算法冲刺】二分

第7类题型:二分

为什么二分题你明明背过模板,一到面试还是容易写出死循环

很多同学第一次刷二分,会觉得它比链表、回溯简单,因为代码看起来就十几行。但真正到面试现场,二分反而特别容易暴露基本功问题。原因不是你不会比较大小,而是你没有真正固定自己的区间定义。

二分题最常见的翻车方式,通常就这几种:

  • 你背过闭区间和左闭右开两个版本,结果写题时混用了。
  • 你知道要算 mid,但边界更新时没有把 mid 排除出去,直接把循环写成死循环。
  • 你能做"找到目标值",一到"找左边界、右边界、插入位置"就开始发虚。

如果你现在处在"模板大概会背,但每次都怕边界写错"的阶段,这一类题一定要单独练透。读完这篇,你至少要把 3 件事彻底练熟:先判断题目有没有单调性、先固定一套区间模板、能在面试里解释为什么当前这次更新不会漏答案。


文章目录


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 + 1mid - 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 个错误

  1. 区间定义没固定,闭区间和左闭右开混着写。
  2. 边界更新没有排除 mid,导致区间不收缩。
  3. 只会标准二分,不会把模板改造成左边界、右边界或插入位置。

5. 代表题精讲 1

题目

704. 二分查找

思路

这是最标准的模板题,核心不是"写出来",而是"每一步都能解释清楚为什么这么写"。

流程非常稳定:

  1. leftright 表示当前候选区间。
  2. 取中点 mid 进行比较。
  3. 命中就直接返回。
  4. 如果 nums[mid] < target,说明目标只可能在右边,left = mid + 1
  5. 如果 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 = midright = mid。因为 mid 已经比较过了,继续保留会导致区间缩不动。

你可以自己代一个最容易挂的样例:nums = [5]target = 2。如果你写成 right = mid,那第一轮 mid 就还是 0,区间不会收缩,循环就容易卡死。

如果这是面试现场,你可以这样说

这题是标准二分模板题,前提是数组有序。我这里统一使用闭区间 [left, right],所以循环条件写 left <= right。每一轮比较 nums[mid]target 后,都能明确排除一半区间;而且边界更新必须写成 mid + 1mid - 1,保证当前比较位置不会被重复保留在区间里。

6. 代表题精讲 2

题目

34. 在排序数组中查找元素的第一个和最后一个位置

思路

这题最重要的认知是:它不是"一次二分找两个答案",而是"两次二分分别找左右边界"。

为什么要拆成两次:

  • 找左边界时,即使命中了 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 个问题:

  1. 这题是不是建立在有序数组或单调性之上。
  2. 我这次统一用闭区间,还是左闭右开。
  3. 我当前是在找"某个值",还是在找"某个边界"。
  4. 命中目标值之后,我是可以返回,还是还要继续收缩区间。

这 4 个问题一旦提前问清楚,二分题大部分边界错误都能少掉一半。

9. 错题本记录方式

二分题建议重点记录:

  • 这题的单调性到底是什么。
  • 我使用的是哪套区间定义。
  • 哪一轮边界更新没有把区间真正缩小。
  • 这题是在找目标值、插入位置,还是左右边界。

10. 面试前 3 分钟速记

  • 二分先看"有序 / 单调"。
  • 先固定一套区间定义,不要混写。
  • mid = left + (right - left) / 2
  • 更新边界时必须排除已经比较过的 mid
  • 找边界题时,关键不是"命中没命中",而是"命中后还要不要继续缩"。

11. 最后总结:二分真正难的不是模板,而是边界纪律

如果你只能记住一句话,那就是:二分题最怕的不是不会写,而是边界定义不统一,导致你自己也不知道当前区间里还剩谁。

面试时你只要按这个顺序去讲,思路就会非常稳:

  1. 先说明这题为什么能二分,是有序还是有单调性。
  2. 再说明你选的是哪套区间定义。
  3. 接着解释当前比较后,为什么能排除左边或右边。
  4. 如果是边界题,再补一句"命中后为什么还不能立刻返回"。

这样你就不只是会背一个模板,而是真的掌握了二分这类题的公共骨架。


上上期速递:【Java实习面试算法冲刺】栈、队列与单调栈

上期速递:【Java实习面试算法冲刺】二叉树

感谢阅读,记得点赞、关注、收藏,欢迎各位评论区交流!!!

相关推荐
rosmis4 分钟前
agent各指标定义
android·java·开发语言
mifengxing7 分钟前
Java 集合进阶(一)
java·开发语言·数据结构·复习笔记
半夢半醒113 分钟前
[原创]《C#高级GDI+实战:从零开发一个流程图》第章:增加菱形、平行四边形、圆角矩形,文本居中显示
算法·c#·流程图
Jerry21 分钟前
LeetCode 90. 子集 II
算法
机器学习之心37 分钟前
CCD实验设计+SVM代理建模+改进NSGA-II工艺参数多目标优化,MATLAB代码
算法·支持向量机·matlab·多目标优化
SOLECA_38 分钟前
Ascend C 算子实战(二)|SigmoidCustom 逐元素激活算子完整开发指南
算法
令狐前生44 分钟前
Intellij IDEA 2025 破解安装
java·ide·intellij-idea
Jerry1 小时前
LeetCode 78. 子集
算法
数聚天成DeepSData1 小时前
数聚天成 DeepSData 数据价值落地实战指南
java·maven·devops
小小小米粒1 小时前
阿姆达尔定律(Amdahl‘s Law)
java·开发语言