Skip to content

题目链接 · 灵神原题解(署名来源)

x=nums[mid] 是现在二分取到的数。

我们需要判断 x 和数组最小值的位置关系,谁在左边,谁在右边?

x 与最后一个数 nums[n1] 比大小:

  • 如果 x>nums[n1],那么可以推出以下结论:
    • nums 一定被分成左右两个递增段;
    • 第一段的所有元素均大于第二段的所有元素;
    • x 在第一段。
    • 最小值在第二段。
    • 所以 x 一定在最小值的左边
  • 如果 xnums[n1],那么 x 一定在第二段。(或者 nums 就是递增数组,此时只有一段。)
    • x 要么是最小值,要么在最小值右边

所以,只需要比较 xnums[n1] 的大小关系,就间接地知道了 x 和数组最小值的位置关系,从而不断地缩小数组最小值所在位置的范围,二分找到数组最小值。

二分基础知识【基础算法精讲 04】

本题视频讲解【基础算法精讲 05】

细节

下面代码用的开区间二分,用其他二分写法也是可以的。

二分的范围可以是 (1,n1),也就是闭区间 [0,n2]

这是因为,如果 nums[n1] 是数组最小值,那么 nums 分成两段,第一段 [0,n2],第二段 [n1,n1],且第一段的所有数都大于 nums[n1]。每次 xnums[n1] 比大小,一定是 x>nums[n1]。这意味着每次二分更新的都是 left,那么循环结束后,答案自然就是 n1 了。

:这里有两个概念「二分范围」和「答案范围」。答案确实可以等于 n1,但对于二分来说,代码中的 if (nums[mid] < nums[n - 1])mid=n1 的时候一定不成立,我们可以直接知道 n1 是蓝色(根据视频中的红蓝染色法),所以 n1 无需在二分区间中。

答疑

:能否与 nums[0] 比大小?

:可以,但要多写一点代码。假设 nums 有两段。如果 nums[mid]>nums[0],那么 mid 在第一段,在最小值左边;否则,mid 要么是最小值,要么在最小值右边。这种写法需要在 (0,n) 中二分,如果二分结果等于 n,说明 nums 其实只有一段,答案是 nums[0]

python
class Solution:
    def findMin(self, nums: List[int]) -> int:
        left, right = -1, len(nums) - 1  # 开区间 (-1, n-1)
        while left + 1 < right:  # 开区间不为空
            mid = (left + right) // 2
            if nums[mid] < nums[-1]:
                right = mid
            else:
                left = mid
        return nums[right]
cpp
// C++ 版待补充
python
class Solution:
    def findMin(self, nums: List[int]) -> int:
        check = lambda i: nums[i] < nums[-1]
        i = bisect_left(range(len(nums) - 1), True, key=check)
        return nums[i]
cpp
// C++ 版待补充
cpp
class Solution {
public:
    int findMin(vector<int>& nums) {
        int left = -1, right = nums.size() - 1; // 开区间 (-1, n-1)
        while (left + 1 < right) { // 开区间不为空
            int mid = left + (right - left) / 2;
            (nums[mid] < nums.back() ? right : left) = mid;
        }
        return nums[right];
    }
};

复杂度分析

  • 时间复杂度:O(logn),其中 nnums 的长度。
  • 空间复杂度:O(1)

思考题

改成计算 nums 的最大值呢?

请读者实现该问题,以加深对本题的理解。

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、二叉树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA/一般树)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

本文整理自灵茶山艾府(endlesscheng)的公开内容,仅供个人学习使用

本站仅供个人学习使用,请勿外传