LeetCode Hot100【二分法-153. 寻找旋转排序数组中的最小值】

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

代码实现
class Solution {
public:
    // 查找旋转排序数组中的最小值
    int findMin(vector<int>& nums) {
        int left = 0, right = nums.size() - 1;  // 定义左右边界
        while (left < right) {  // 当左边界小于右边界时继续搜索
            int mid = left + (right - left) / 2;  // 计算中间位置
            if (nums[mid] < nums[right]) {  // 如果中间元素小于右边界元素,说明最小值在左半部分
                right = mid;  // 收缩右边界
            } else {  // 如果中间元素大于等于右边界元素,最小值在右半部分
                left = mid + 1;  // 收缩左边界
            }
        }
        return nums[right];  // 返回最小值
    }
};

执行流程

示例输入
nums = [4, 5, 6, 7, 0, 1, 2]
步骤解析
  1. 初始化 left = 0, right = 6(数组的索引范围)。
  2. 第一次循环
    • mid = (0 + 6) / 2 = 3,对应元素 nums[3] = 7
    • nums[mid] = 7 > nums[right] = 2,说明最小值在右半部分,更新 left = mid + 1 = 4
  3. 第二次循环
    • mid = (4 + 6) / 2 = 5,对应元素 nums[5] = 1
    • nums[mid] = 1 < nums[right] = 2,说明最小值在左半部分,更新 right = mid = 5
  4. 第三次循环
    • left = 4, right = 5mid = (4 + 5) / 2 = 4,对应元素 nums[4] = 0
    • nums[mid] = 0 < nums[right] = 1,说明最小值在左半部分,更新 right = mid = 4
  5. 结束条件left = right = 4,返回 nums[right] = 0

最终输出:

0

关键思路

  1. 旋转排序数组的性质
    • 旋转排序数组是一个已经排序的数组,在某个位置发生旋转,形成两个有序子数组。
    • 最小值一定处在两个有序子数组的交界处。
  2. 二分查找优化
    • 与传统的二分查找不同,旋转排序数组中每次比较的是中间元素与右边界元素(nums[mid] < nums[right])。
    • 如果中间元素小于右边界元素,说明最小值在左半部分,因此更新 right = mid
    • 如果中间元素大于等于右边界元素,说明最小值在右半部分,因此更新 left = mid + 1
  3. 终止条件
    • leftright 相遇时,leftright 指向的就是最小值的位置。

时间 & 空间复杂度

操作 时间复杂度 空间复杂度
查找最小值 O(log⁡n) O(1)
  • 时间复杂度: 每次通过二分查找将搜索范围缩小一半,因此时间复杂度是 O(log⁡n),其中 n是数组的长度。
  • 空间复杂度: 只使用了常数空间来存储 left, right, mid,因此空间复杂度是 O(1)。

基础语法解析

1. int mid = left + (right - left) / 2;

  • 避免溢出的中间位置计算: 使用 left + (right - left) / 2 计算中间位置,避免 (left + right) / 2 可能造成的溢出问题。
int mid = left + (right - left) / 2;

2. if (nums[mid] < nums[right])

  • 判断最小值在左半部分还是右半部分: 如果 nums[mid] 小于 nums[right],说明最小值在左半部分,否则在右半部分。
if (nums[mid] < nums[right]) {
    right = mid;
} else {
    left = mid + 1;
}

3. return nums[right];

  • 返回最小值:left == right 时,right 指向的是最小值的元素,因此直接返回 nums[right]
return nums[right];

优化 & 变种

优化 1:处理数组中有重复元素的情况

  • 如果数组中存在重复元素(例如 [2, 2, 2, 0, 1]),则可以在比较时避免直接判断 nums[mid] < nums[right],改为 if (nums[mid] <= nums[right]),此时需要增加一个条件来判断如何处理重复元素。
if (nums[mid] <= nums[right]) {
    right = mid;
} else {
    left = mid + 1;
}

变种 1:查找旋转数组的最大值

  • 如果要查找旋转排序数组中的最大值,可以对上面的方法进行改进,类似的二分查找方法,比较中间元素与右边界元素,只不过最大值在相反的方向。

总结

C++ 语法 / 结构 作用
mid = left + (right - left) / 2 安全计算中间位置,避免溢出
if (nums[mid] < nums[right]) 判断最小值在左半部分还是右半部分
return nums[right] 返回最小值
时间复杂度: O(log n) 每次二分查找的时间复杂度
空间复杂度: O(1) 使用常数空间

你可能感兴趣的:(leetcode,算法,排序算法)