LeetCode Hot 100 题解 · 二分篇

LeetCode Hot 100 题解 · 二分篇

  1. 本专题收录 LeetCode Hot 100 中所有 二分查找 相关题目。
  2. 每道题提供最直接、最容易理解的解题思路,包含详细注释的代码实现,方便笔试和面试复习。

前置:二分的写法和形式,先想清楚"找什么"

二分不是"排好序才能用",本质是在一个单调的判定上砍半 :能回答"答案在 mid 的哪一边",就能每次丢掉一半。写二分翻车基本都翻在两件事上------没想清楚到底在找什么、区间形式和收缩方式不配套。先定目标,再定形式,剩下的全是推出来的,不用背。

1. 三种区间形式

形式 初始化 循环条件 收缩方式 适合找什么
闭区间 l = 0, r = n - 1 l <= r 命中即 return;找边界配 l = mid + 1 / r = mid - 1 一个具体的数(可能不存在)
左闭右开 l = 0, r = n l < r r = mid / l = mid + 1,出循环 l 即答案 第一个满足条件的位置
红蓝开区间(本篇统一用) l = -1, r = n l + 1 < r r = mid / l = mid,不用 ±1 左右边界都行,心智负担最小

2. 红蓝开区间写法(l = -1, r = n

把二分看成染色:数组每个位置要么"满足条件"(蓝),要么"不满足"(红)。维护两个边界:

java 复制代码
int l = -1, r = n;            // 不变量:[0, l] 全是"否"(红),[r, n-1] 全是"是"(蓝)
while (l + 1 < r) {           // 中间还有没染色的格子
    int mid = l + (r - l) / 2;
    if (check(mid)) {
        r = mid;              // mid 染蓝:第一个"是"最靠前也就是 mid
    } else {
        l = mid;              // mid 染红:最后一个"否"最靠后也就是 mid
    }
}
// 出循环 l + 1 == r:l = 最后一个"否",r = 第一个"是"

三个性质,决定了它为什么好用:

  1. 初始化天然成立l = -1r = n 是两个"虚拟"哨兵位,左侧红的集合和右侧蓝的集合都是空------不用验证 nums[0]nums[n-1] 满不满足条件。
  2. 绝不死循环 :循环内 l + 1 < r 保证 mid 严格落在 (l, r) 开区间里,l = mid 严格变大、r = mid 严格变小,区间每轮必缩。闭区间写法的 l = mid 死循环问题(mid == l 时不动)在这里被结构性消灭。
  3. 一个模板两种答案 :要"第一个满足 "的位置取 r;要"最后一个满足 "的位置取 l------34.在排序数组中查找元素的第一个和最后一个位置 正好两头都要。

要查"具体值存不存在",加一个 == 分支提前 return(35.搜索插入位置 的写法);红蓝式还能直接用在旋转数组 这种"整体无序但方向可判"的场上(33.搜索旋转排序数组)。

3. 三个通用提醒

  • mid = l + (r - l) / 2l + r 溢出------数学上和 (l + r) / 2 等价,但前者不会先溢出。
  • 收缩用 l = mid(不带 +1)时,mid 必须能严格大于 l ,否则死循环。红蓝开区间的下取整天然安全;但区间收成 while (left < right)left = i 时就要上取整i = left + (right - left + 1) / 2)------4.寻找两个正序数组的中位数 的写法,坑和救法都在这。
  • 判定条件"单调"比"数组有序"宽得多:旋转数组"target 在 mid 的哪一边"可判、4.寻找两个正序数组的中位数 里"切点左边元素个数够不够"也是单调的。

📑 目录

题号 题目 难度 核心思路
35 35.搜索插入位置 🟢简单 红蓝开区间的入门:三分支精确查找 ------== 提前 return,< 染红 l = mid> 染蓝 r = mid。不变量是 [0, l]< target[r, n)> target:存在时中途命中;不存在时 lr 把 target 在中间,r 正是插入位置。
74 74.搜索二维矩阵 🟠中等 从右上角出发的 Z 字形查找 :当前值比 target 大就左移(排除一整列,下面只会更大),比 target 小就下移(排除一整行,左边只会更小),每步排掉一行或一列, O ( m + n ) O(m+n) O(m+n)。它只要求"行有序 + 列有序",好写好记。要满足进阶 O ( log ⁡ ( m × n ) ) O(\log (m \times n)) O(log(m×n)) 得认出矩阵其实是一维有序数组蜷起来的 ,拉平下标一次二分(mid / n 定行、mid % n 定列)。
34 34.在排序数组中查找元素的第一个和最后一个位置 🟠中等 精确查找碰到哪个下标全凭运气,要边界 就得换目标:首位置 = 第一个 ≥ target (取 r),尾位置 = 最后一个 ≤ target (取 l)------两个对称目标,同一个红蓝模板改一个比较符跑两遍,最后各验一次值兜底。
33 33.搜索旋转排序数组 🟠中等 旋转数组左大右小两段 ,拿最右元素 back(第二段最大值)当参照:> back 是第一段、≤ back 是第二段。每次看 targetnums[mid] 谁在左边------同段直接比值 (段内升序),异段看段位(第一段整体在第二段左边),六种组合就一个判定,红蓝收缩到收敛点再验证。
153 153.寻找旋转排序数组中的最小值 🟠中等 最小值 = 第二段的头 。判定只看 nums[mid] 和最右元素:> 说明 mid 还在第一段(断崖在右边)染红, 说明 mid 已在第二段(自己可能就是最小)染蓝,r 收敛到第二段第一个元素。和 l 比不行------nums[mid] > nums[l] 区分不出"没旋转"和"mid 在第一段",方向不唯一;和最右比每种情况方向唯一。
4 4.寻找两个正序数组的中位数 🔴困难 中位数 = 合并后左右两半等长的切点 。在短数组上二分切点 ij 由"左半凑够 totalLeft 个"唯一解出):i 越大 nums1[i-1] 越大、nums2[j] 越小,所以 nums1[i-1] > nums2[j]i 单调------只查这一个不等式 ,二分找最后一个满足它的 i(短数组保证另一个方向自动成立),上取整防死循环,正负无穷当空侧哨兵。 O ( log ⁡ min ⁡ ( m , n ) ) O(\log \min(m,n)) O(logmin(m,n))。

35.搜索插入位置

题目链接35.搜索插入位置

题目描述 :给一个升序 排列的数组 nums(无重复元素)和目标值 target,如果存在就返回下标;不存在返回它按顺序插入后的位置 。要求 O ( log ⁡ n ) O(\log n) O(logn)。


思路一:红蓝开区间三分支(== 提前命中,lr 把 target 夹出来)

核心思想

  1. 红蓝开区间最直接的用法,三个分支各管一件事:== 提前 return (存在的话根本不用等收敛);< target 染红 l = mid> target 染蓝 r = mid
  2. 不变量是:[0, l] 全部 < target[r, n) 全部 > targettarget 存在时会被中途命中 ;不存在时,红蓝边界收敛到它"该在的位置"两侧------r 就是第一个 > target 的位置,正是插入位置(后面所有人都得给它让位)。
  3. 所有元素都比 target 小时 r 收敛到 n------正好是插到末尾的位置,不用特判。一个模板同时覆盖"存在"和"不存在",不用分两种情况讨论。

时间复杂度 : O ( log ⁡ n ) O(\log n) O(logn),其中 n 为数组长度。

空间复杂度 : O ( 1 ) O(1) O(1)。

java 复制代码
class Solution {
    public int searchInsert(int[] nums, int target) {
        int n = nums.length, l = -1, r = n;
        // 不变量:[0, l] 全 < target,[r, n) 全 > target
        while (l + 1 < r) {
            int mid = l + (r - l) / 2;
            if (nums[mid] > target) {
                r = mid; // mid 及其右侧都 > target:插入位在 mid 左边(含 mid)
            } else if (nums[mid] < target) {
                l = mid; // mid 及其左侧都 < target:插入位在 mid 右边
            } else {
                return mid; // 命中:target 存在,就是它
            }
        }
        return r; // 收敛点:第一个 > target 的位置 = 插入位置
    }
}

这版等价于"找第一个 ≥ target 的下标":无重复元素下,target 不存在时"第一个 >"和"第一个 "是同一个位置。后者是更通用的抽象(有重复时依然成立),34.在排序数组中查找元素的第一个和最后一个位置 就靠它。


74.搜索二维矩阵

题目链接74.搜索二维矩阵

题目描述 :给一个 m × n 矩阵:每行从左到右升序 ,且每行第一个整数大于前一行的最后一个整数 。再给 target,判断它在不在矩阵里。


思路一:Z 字形查找(右上角起步,每步排掉一行或一列)

核心思想

  1. "每行首元素大于上一行尾元素"蕴含两件事:每行有序 ,且每列从上到下递增 。站在右上角 看:往左一路变小(本行),往下一路变大(本列)------当前位置是一行里最大的、一列里最小的,一大一小两个方向都可控
  2. 于是每步都能排除一片:当前值 < target → 本行左边全更小,整行作废,下移;当前值 > target → 本列下面全更大,整列作废,左移。每步至少排掉一行或一列,最坏 m + n 步出结果。
  3. 起点必须是右上或左下------左上角是"双小"(比 target 小时不知道该往哪走)、右下角是"双大",只有对角的两端才能双向排除。
  4. 这写法只依赖"行有序 + 列有序"240.搜索二维矩阵 II 的弱条件也够用),好写好记,首选。代价是 O ( m + n ) O(m + n) O(m+n) 摸不到进阶的 O ( log ⁡ ( m × n ) ) O(\log (m \times n)) O(log(m×n))------那得上思路二。

时间复杂度 : O ( m + n ) O(m + n) O(m+n),其中 m、n 为矩阵行数、列数,每步下移或左移一格,最多走 m + n - 1 步。

空间复杂度 : O ( 1 ) O(1) O(1)。

java 复制代码
class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int m = matrix.length, n = matrix[0].length;
        int r = 0, c = n - 1; // 从右上角出发:本行最大、本列最小,两个方向都可控
        while (r < m && c >= 0) {
            int cur = matrix[r][c];
            if (cur < target) {
                ++r; // 本行左边全更小,整行作废,下移
            } else if (cur > target) {
                --c; // 本列下面全更大,整列作废,左移
            } else {
                return true;
            }
        }
        return false; // 走出边界还没碰上:不存在
    }
}

思路二:拉平成一维做一次二分(满足 O ( log ⁡ ( m × n ) ) O(\log (m \times n)) O(log(m×n)) 的进阶)

核心思想 :题目的强条件("每行首元素大于上一行尾元素")意味着按行优先读整个矩阵,就是一个严格递增的一维数组 ------矩阵只是它蜷成 m 行的形状。直接当一维数组二分:mid 是一维下标,映射回二维就是 行 = mid / n列 = mid % n。Z 字形没用上这个强条件(它弱条件就能跑),条件越强、能上的解法越快,这正是两道矩阵搜索题的分界线。

时间复杂度 : O ( log ⁡ ( m × n ) ) O(\log (m \times n)) O(log(m×n)),等价于对长度 m×n 的一维数组二分。

空间复杂度 : O ( 1 ) O(1) O(1)。

java 复制代码
class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int m = matrix.length, n = matrix[0].length;
        int l = -1, r = m * n; // 红蓝开区间,把矩阵当一维数组 [0, m*n-1]
        while (l + 1 < r) {
            int mid = l + (r - l) / 2;
            int val = matrix[mid / n][mid % n]; // 一维下标映射回二维:商是行,余是列
            if (val < target) {
                l = mid;
            } else {
                r = mid; // >= target 染蓝:r 收敛到第一个 >= target 的位置
            }
        }
        return r < m * n && matrix[r / n][r % n] == target; // 收敛点验证
    }
}

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

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

题目描述 :给一个非递减 (有重复)的升序数组 numstarget,返回它在数组里出现的第一个和最后一个下标 (闭区间 [first, last]);不存在返回 [-1, -1]。要求 O ( log ⁡ n ) O(\log n) O(logn)。


思路一:两次红蓝二分(≥ targetr 找首、≤ targetl 找尾)

核心思想

  1. 数组有重复,精确查找碰到哪个下标全凭运气------要边界就得换目标。首位置 = 第一个 ≥ target 的下标 :红蓝跑完取 r;若这个位置的值恰好是 target,它就是首,值更大说明 target 根本不存在。
  2. 尾位置 = 最后一个 ≤ target 的下标 :判定条件改成 染红的方向,跑完取 l;若 l 处的值恰好是 target,它就是尾。两个目标完全对称 (一个找第一个"是"、一个找最后一个"是"),同一个红蓝模板只改一个比较符,跑两遍。
  3. 两次结果各验一次值兜底:首位置越界(r == n,全比 target 小)或值不等,返回 [-1, -1];尾位置同理。验证不能省------二分只保证"条件边界",不保证 target 真的存在。

时间复杂度 : O ( log ⁡ n ) O(\log n) O(logn),其中 n 为数组长度,两次二分。

空间复杂度 : O ( 1 ) O(1) O(1)。

java 复制代码
class Solution {
    public int[] searchRange(int[] nums, int target) {
        int n = nums.length;

        // 第一趟:找第一个 >= target(取 r)
        int l = -1, r = n;
        while (l + 1 < r) {
            int mid = l + (r - l) / 2;
            if (nums[mid] < target) {
                l = mid; // 染红:[0, l] 全 < target
            } else { // >= 就动右边
                r = mid; // 染蓝:[r, n) 全 >= target,r = 第一个 >= target
            }
        }
        int start = r;
        if (start >= n || nums[start] != target) {
            start = -1; // 越界(全比它小)或落点不是它:不存在
        }

        // 第二趟:找最后一个 <= target(取 l),比较符号反过来
        l = 0; // 升序保证:nums[0] > target 时后面全 > target,r 会收到 0,值验证兜底
        r = n;
        while (l + 1 < r) {
            int mid = l + (r - l) / 2;
            if (nums[mid] <= target) {
                l = mid; // 染红:[0, l] 全 <= target,l = 最后一个 <= target
            } else { // > 就动右边
                r = mid;
            }
        }
        int end = l;
        if (end >= n || nums[end] != target) {
            end = -1;
        }
        return new int[] { start, end };
    }
}

"最后一个 ≤ target"比"第一个 > target 退一格"更直接------不用退格,也不用 target + 1 这种"查一个不存在的数"的技巧,红蓝模板取 l 天然就是"最后一个满足"。一套模板、一个符号之差,左边界右边界都有了,这就是红蓝式的第三个好处。


33.搜索旋转排序数组

题目链接33.搜索旋转排序数组

题目描述 :一个原本升序、元素互不相同 的数组,在某个未知下标处旋转 了一次(前段挪到后面),比如 [0,1,2,4,5,6,7] 旋转成 [4,5,6,7,0,1,2]。给旋转后的数组和 target,返回 target 的下标,不在返回 -1。要求 O ( log ⁡ n ) O(\log n) O(logn)。


思路一:红蓝二分判相对位置(同段比值、异段看段位)

核心思想

  1. 旋转数组是左大右小两段 (第一段整体大于第二段,第二段最大值就是数组最右元素 back = nums[n-1])。back 是天然的段判据:> back 是第一段,≤ back 是第二段------一个问题"你在哪段"一个比较就答了。
  2. 二分要判的是:target 在 nums[mid] 的左边还是右边 (旋转意义下的位置关系)。两种情况:
    • 同段 :段内各自升序,直接比值------cur >= target 则 target 在 mid 左边(或就是 mid),否则在右边;
    • 异段:段位定方向------第一段整体在第二段左边。target 在第一段、cur 在第二段 → target 在 cur 左边;反过来在右边。
  3. target 段位 × cur 段位 × 段内大小,展开正好六种组合 ,每种都唯一给出"往左(r = mid)"或"往右(l = mid)"------本质就是一句"target 在 cur 的哪边"。红蓝收缩到收敛点 r,验证 nums[r] == target
  4. 每轮区间确定地砍半, O ( log ⁡ n ) O(\log n) O(logn) 保住。写的时候六分支别抄:记住"同段比值、异段看段位",现场推每一支,比背模板稳。

时间复杂度 : O ( log ⁡ n ) O(\log n) O(logn),其中 n 为数组长度。

空间复杂度 : O ( 1 ) O(1) O(1)。

java 复制代码
class Solution {
    public int search(int[] nums, int target) {
        int l = -1, r = nums.length;
        // 左大右小:back = 最右元素 = 第二段最大值,是划分两段的参照
        int back = nums[r - 1];
        while (l + 1 < r) {
            int mid = l + (r - l) / 2;
            int cur = nums[mid];
            // 判定只有一句:target 在 cur 左边 -> r = mid;在右边 -> l = mid
            // 同段(都 <= back 或都 > back)比大小,异段看段位(第一段整体在左)
            if (target <= back && cur <= back && cur >= target) {
                r = mid; // 同在二段,cur >= target:target 在 cur 左或即 cur
            } else if (target <= back && cur <= back && cur < target) {
                l = mid; // 同在二段,cur < target
            } else if (target <= back && cur > back) { // 太大了
                l = mid; // target 在二段,cur 在一段:target 在右边
            } else if (target > back && cur > back && cur >= target) {
                r = mid; // 同在一段,cur >= target
            } else if (target > back && cur > back && cur < target) {
                l = mid; // 同在一段,cur < target
            } else {
                r = mid; // target 在一段,cur 在二段:target 在左边
            }
        }
        return r < nums.length && nums[r] == target ? r : -1; // 收敛点验证
    }
}

本题元素互不相同 是关键前提------有重复时 cur == back == target 之类的相等把"哪段"判糊了,得退化成 l++ 逐个挪(那是 81.搜索旋转排序数组 II,最坏 O ( n ) O(n) O(n))。面试要能主动说出这个区别。


153.寻找旋转排序数组中的最小值

题目链接153.寻找旋转排序数组中的最小值

题目描述 :一个原本升序、元素互不相同 的数组旋转过一次(可能没转)。不要求找到 target ,直接返回数组里的最小值 。要求 O ( log ⁡ n ) O(\log n) O(logn)。


思路一:和最右元素比段,红蓝收敛到第二段的头

核心思想

  1. 旋转数组两段各自升序、第一段整体大于第二段------最小值就是第二段的头 。红蓝二分直接找它:染蓝条件 = "nums[mid] 已在第二段"。
  2. 判定只比一件事:nums[mid]最右元素 (第二段最大值)。nums[mid] > back------mid 在第一段,断崖在右边,mid 染红 l = midnums[mid] <= back------mid 在第二段(或没旋转),mid 自己可能就是最小,染蓝 r = mid
  3. 为什么和最右比不和最左比nums[mid] > nums[0] 区分不出"没旋转"(mid 就是最小或其后)和"mid 在第一段"(最小在远处右边)------两种情况方向完全相反。和最右比则每种情况方向唯一,这是本题最巧的一步。
  4. r 初始给 n - 1(不是 n):答案是一个真实存在的元素,最右元素天然"在第二段",蓝侧哨兵从真元素起步即可,出循环 nums[r] 直接就是最小值。

时间复杂度 : O ( log ⁡ n ) O(\log n) O(logn),其中 n 为数组长度。

空间复杂度 : O ( 1 ) O(1) O(1)。

java 复制代码
class Solution {
    public int findMin(int[] nums) {
        // 一定是两段(或没转),左边的比右边的大,找的就是右边那段的最左边的值
        int l = -1, r = nums.length - 1; // r 从真元素起步:nums[n-1] 必在第二段
        while (l + 1 < r) {
            int mid = l + (r - l) / 2;
            if (nums[mid] > nums[nums.length - 1]) {
                l = mid; // mid 在第一段:断崖在右边,最小值在 (mid, r]
            } else {
                r = mid; // mid 在第二段(或没旋转):mid 自己可能就是最小,染蓝
            }
        }
        return nums[r]; // 收敛点 = 第二段第一个元素 = 最小值
    }
}

33.搜索旋转排序数组 是同一套"旋转数组"框架的两道门:33 用最右元素判target 的位置方向 (六分支),153 用最右元素判mid 的段位 (一个比较)------参照物都是 nums[n-1],一起复习对比着记。


4.寻找两个正序数组的中位数

题目链接4.寻找两个正序数组的中位数

题目描述 :给两个升序 数组 nums1nums2,长度分别是 mn,返回合并后 的中位数。要求时间复杂度 O ( log ⁡ ( m + n ) ) O(\log (m+n)) O(log(m+n))。进阶: O ( log ⁡ min ⁡ ( m , n ) ) O(\log \min(m, n)) O(logmin(m,n))。


思路一:二分短数组的切点(只查一个不等式,找最后一个合法的 i)

核心思想

  1. 中位数把合并数组切成元素个数确定的左右两半 :左半要凑 totalLeft = (m + n + 1) / 2 个(奇数时多的放左边,奇偶统一)。切点定了,中位数只由"左半最大值右半最小值"决定。
  2. 合并切缝由两个数组各自的切点拼成:nums1i 个进左半、nums2j 个进左半,i 定了 j 就被唯一确定j = totalLeft - i)------所以只需要在一个数组上二分。在短的那个上二分j 不会越界(短数组是"调节变量",长数组兜底),这就是 O ( log ⁡ min ⁡ ( m , n ) ) O(\log \min(m,n)) O(logmin(m,n)) 的由来。
  3. 合法的切缝满足交叉不大于nums1[i-1] <= nums2[j]nums2[j-1] <= nums1[i]。妙处在单调性:i 越大 nums1[i-1] 越大、nums2[j] 越小(ji 减小)------第一个不等式对 i 单调 。于是二分目标是清晰的:找最后一个满足 nums1[i-1] <= nums2[j]i
  4. 只查第一个不等式就够吗?够,短数组保证自动成立:取到最大的合法 i 后,i + 1 处(若存在)必然 nums1[i] > nums2[j-1]------恰好就是第二个不等式"反向成立"的形态;i 取到边界 m 时右半为空、正无穷兜底。一个方向的检查隐含了另一个方向,这是本解法最省事的一步。
  5. 两个工程细节:mid上取整i = left + (right - left + 1) / 2)------收缩写的是 left = i,下取整可能 i == left 造成死循环,上取整保证 i 严格大于 left;边界用正负无穷 当哨兵------i = 0 左半为空取 -∞i = m 右半为空取 +∞,四侧比较不用特判空。
  6. 收敛出合法切缝后:奇数总数答案是 max(左半两个最大),偶数总数是 (左半最大 + 右半最小) / 2.0

时间复杂度 : O ( log ⁡ min ⁡ ( m , n ) ) O(\log \min(m, n)) O(logmin(m,n)),其中 m、n 为两数组长度,二分范围是短数组的切点。

空间复杂度 : O ( 1 ) O(1) O(1)。

java 复制代码
class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        // 1. 保证 nums1 是更短的数组,二分只在短数组做,减少循环次数
        if (nums1.length > nums2.length) {
            int[] temp = nums1;
            nums1 = nums2;
            nums2 = temp;
        }
        // m:短数组长度,n:长数组长度
        int m = nums1.length;
        int n = nums2.length;
        // 二分左右边界:i 可以取 0 ~ m
        int left = 0, right = m;
        // 合并后左边一共需要放多少个数字
        // 加1是为了奇数时左边多一个,中位数直接取左边最大值
        int totalLeft = (m + n + 1) / 2;

        // 二分循环,找到最后一个满足 nums1[i-1] <= nums2[j] 的切割点 i
        while (left < right) {
            // 取中间切割位置,向上取整,避免死循环(left = i 时 i 必须严格大于 left)
            int i = left + (right - left + 1) / 2;
            // 根据左边总数量,算出 nums2 需要切到 j 位置:i 定了 j 唯一确定
            int j = totalLeft - i;
            // 分割不合法:nums1 左边最大值 > nums2 右边最小值
            // 说明 nums1 切太靠右,数字太大,切割点往左移
            if (nums1[i - 1] > nums2[j]) {
                right = i - 1;
            } else {
                // 该不等式成立:i 还可能更大,继续往右试探
                left = i;
            }
        }
        // 循环结束 left == right,就是最终切割位置 i(最后一个合法的)
        int i = left;
        int j = totalLeft - i;

        // 处理四种边界情况,切割在数组开头/末尾用无穷值代替
        // nums1 左半边最大值
        int nums1Left = i == 0 ? Integer.MIN_VALUE : nums1[i - 1];
        // nums1 右半边最小值
        int nums1Right = i == m ? Integer.MAX_VALUE : nums1[i];
        // nums2 左半边最大值
        int nums2Left = j == 0 ? Integer.MIN_VALUE : nums2[j - 1];
        // nums2 右半边最小值
        int nums2Right = j == n ? Integer.MAX_VALUE : nums2[j];

        // 判断总长度奇偶,计算中位数
        if ((m + n) % 2 == 1) {
            // 奇数:左边多一个,中位数是左边两段最大的数
            return Math.max(nums1Left, nums2Left);
        } else {
            // 偶数:左边最大 和 右边最小 的平均值,除以2.0保证浮点
            return (Math.max(nums1Left, nums2Left) + Math.min(nums1Right, nums2Right)) / 2.0;
        }
    }
}

面试可以退一档的写法:对 k = (m+n+1)/2 做"二分扔掉第 k 小的一半 "------每轮比较两数组第 k/2 个元素,扔掉小的那 k/2 个、k 减半, O ( log ⁡ ( m + n ) ) O(\log (m+n)) O(log(m+n))。好讲好写,但达不到进阶的 O ( log ⁡ min ⁡ ( m , n ) ) O(\log \min(m,n)) O(logmin(m,n)),讲完主动补一句"还可以在短数组上二分切点做到 log ⁡ min ⁡ \log \min logmin",加分点就到了。


总结:二分篇的主线是把"找什么"定义清楚,再让区间每轮确定地砍半35.搜索插入位置 红蓝三分支,lr 把 target 夹出来,存在命中不存在夹位;74.搜索二维矩阵 右上角 Z 字形每步排掉一行一列,进阶认出矩阵是一维数组蜷起来的、拉平一次二分;34.在排序数组中查找元素的第一个和最后一个位置 同一个红蓝模板改一个比较符跑两遍,r 找首、l 找尾;33.搜索旋转排序数组 拿最右元素当段判据,同段比值、异段看段位,六分支一个判定;153.寻找旋转排序数组中的最小值 同一个参照只判 mid 的段位,r 收敛到第二段的头;4.寻找两个正序数组的中位数 在短数组上二分切点,靠单调性只查一个不等式、上取整防死循环、正负无穷当哨兵。核心都是一句话:答案所在的判定是单调的,每轮确定地丢掉一半------排序只是单调性最常见的一件外衣。

相关推荐
hqyjzsb1 小时前
零 AI 项目经验,学 Python 转型 AI 的正确顺序是什么?
开发语言·人工智能·python·算法·职场和发展·数据挖掘·数据分析
微功夫信息技术1 小时前
分层多智能体强化学习驱动的非急救转运公平 - 效率统一调度系统研究与实践
人工智能·学习·算法·动态规划
小羊没烦恼!2 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
sanjiaomao3332 小时前
从论文公式到Python实现:用单元测试校验滑动平均算法
python·算法·单元测试
乐迪信息2 小时前
如何通过AI防爆摄像机精准判断船舶超速?
大数据·人工智能·算法·安全·目标跟踪
小荷才露尖尖角,早有蜻蜓立上头3 小时前
过滤器的4种创建方式
java
大圣编蚕3 小时前
Java ByteArrayInputStream 详解:从入门到实战
java·开发语言·算法
程序员阿明3 小时前
idea把插件啥的不默认放在C盘
java·ide·intellij-idea
linweidong4 小时前
中科闻歌Java面试题及参考答案
java·python·大模型·提示词·ai agent·mcp·rag原理