【Hot 100】153. 寻找旋转排序数组中的最小值

请添加图片描述

  • 🙋‍♂️ 作者:海码007
  • 📜 专栏:算法专栏
  • 💥 标题:【Hot 100】153. 寻找旋转排序数组中的最小值
  • ❣️ 寄语:书到用时方恨少,事非经过不知难!

引言

这一道题目和上一道题目是一样的。

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

  • 🎈 题目链接:
  • 🎈 做题状态:

我的解题

旋转后的数组其实就是两个升序数组,也可能就一个,因为旋转n次又回来了。
还是使用二分法来找最小值,每次将 mid 和 right 的元素进行比较:

  1. 如果 nums[mid] < nums[right] 则说明 最小值在左侧区间。mid可能是最小值。
  2. 如果 nums[mid] > nums[right] 则说明最小值在右侧区间,mid不可能是最小值。
class Solution {
public:
    int findMin(vector<int>& nums) {
        int n = nums.size();
        if (n == 1) return nums[0];
        if (n == 2) return nums[0] < nums[1] ? nums[0] : nums[1];

        int left = 0;
        int right = n - 1;

        // 一般二分法的循环结束条件是 left <= right 而这里当 left == right 时就找到最小值了
        while (left < right)
        {
            int mid = left + (right - left) / 2;
            if (nums[mid] < nums[right])	// 这两个数不会相等。
            {
                right = mid;
            } else
            {
                left = mid + 1;
            }
        }

        return nums[left];
    }
};

mid 可以和 left 比较吗?

虽然 **midleft 的比较也能确定最小值的区间,但在旋转排序数组中,比较 midright 通常更直观和有效。让我更详细地解释为什么这样做更合适。

  1. 比较 midleft 的方式:

确实,通过比较 midleft,你也能确定最小值的位置。特别是:

  • 如果 nums[mid] > nums[left],那么最小值一定在右半部分(因为 leftmid 一定是升序的)。
  • 如果 nums[mid] < nums[left],那么最小值一定在左半部分(因为 leftmid 中间部分发生了旋转)。

但这种方法在 nums[mid] == nums[left] 时,不好进一步缩小区间。

2. 问题的关键在于 nums[mid] == nums[left] 的情况

  • 如果 nums[mid] == nums[left],我们就无法判断是应该把左半部分还是右半部分作为下一步查找的目标。
  • 在这种情况下,无法直接决定最小值是在左侧还是右侧,因为这两个值相等。因此,我们需要小心地处理这种情况,通常通过增加 left 来避免死循环,虽然这会导致 O(n) 的最坏时间复杂度。

3. 比较 midright 的优势:

相比之下,比较 midright 是一个更简单和直观的选择,因为:

  • nums[mid] < nums[right] 时,意味着右边是升序的,所以最小值一定在左边(包括 mid)。
  • nums[mid] > nums[right] 时,意味着最小值在右边(mid 不可能是最小值),所以我们可以直接将 left = mid + 1

这种方式不需要处理 nums[mid] == nums[left] 的特殊情况,因为在整数的除法是向下取整的,所以mid只可能等于left(这道题目的前提是nums中的数字都唯一)。

总结:

  1. midleft 的比较:可以确定最小值的位置,但需要特别小心 nums[mid] == nums[left] 的情况。如果处理不当,会导致时间复杂度退化为 O(n)
  2. midright 的比较:通常更简洁有效,因为它避免了上述问题,并能在 O(log n) 时间内找到最小值。

代码优化

class Solution {
public:
    int findMin(vector<int>& nums) {
        int left = 0;
        int 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[left];
    }
};

这个方法没有处理 nums[mid] == nums[left] 的特殊情况,因此更加高效和简单。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值