二分查找(寻找旋转排序数组中的最小值)(7)

https://blog.csdn.net/2601_95366422/article/details/158771447

上节课链接

一.题目

153. 寻找旋转排序数组中的最小值 - 力扣(LeetCode)

二.思路讲解

第一步:找二分性

题目给出的数组原本是升序排列 ,经过若干次旋转后,变成了由两段升序子数组拼接而成的形式:前一段的所有元素都大于 后一段的所有元素(除非旋转次数为数组元素个数的倍数,即整个数组仍然是升序)。这种结构天然具备二分性 :我们可以通过比较中间元素与数组最右边的元素,来判断中间元素位于前一段还是后一段,从而确定最小值所在的区间。即使数组完全升序(即没有旋转),也可以看作后一段为空,此时最小值就是第一个元素。因此,无论是否旋转,数组都满足"可以通过比较缩小范围"的条件,从而可以使用二分查找。

第二步:采用左端点还是右端点

我们要找的是数组中的最小值 。在旋转数组中,最小值就是后一段的第一个元素,也就是第一个小于等于最右边元素的元素(因为后一段所有元素都小于前一段,且后一段本身是升序)。因此,我们需要找到第一个满足"小于等于最右边元素"的位置 ,这正是左端点二分查找 的典型应用。采用左端点模板,中点取向下取整 ,缩进规则为:当中间值大于最右边元素时,说明中间值在前一段,最小值在右边,left = mid + 1;否则(中间值小于等于最右边元素),说明中间值在后一段或就是最小值,right = mid。循环结束时,left 即为最小值的索引。

第三步:为什么选择最右边的元素作为基准值

选择数组最右边的元素作为基准值,是因为它位于数组末尾,且具有明确的特性:在旋转数组中,最右边的元素一定是后一段的最后一个元素(如果数组没有旋转,它就是整个数组的最大值)。通过比较中间值与最右边元素,可以准确判断中间值属于哪一段:

  • 如果 nums[mid] > nums[right](其中 right 指向最右边),说明中间值在前一段(因为前一段所有元素都大于后一段),那么最小值一定在 mid 的右侧。

  • 如果 nums[mid] <= nums[right],说明中间值在后一段(包括可能的最小值),那么最小值在 mid 左侧或就是 mid 本身。 这种比较方式简单有效,且能统一处理旋转和未旋转的情况。如果选择最左边的元素作为基准,则需要考虑更多边界情况,因此最右边的元素是更优的选择。

三.代码演示

cpp 复制代码
class Solution {
public:
    int findMin(vector<int>& nums) 
    {
        int left = 0;
        int n = nums.size();
        int right = n - 1;
        int x = nums[right];
        while(left <right)
        {
            int mid = left + (right - left) /2;
            if(nums[mid] > x)
                left = mid + 1;
            else
                right = mid;
        }
        return nums[left];
    }
};

四.代码讲解

一、循环条件与中点计算

采用 while (left < right) 作为循环条件,而不是 left <= right。这是因为当左右指针相遇时,区间内只剩一个元素,此时无需继续二分 ,可以直接在循环外返回该元素。这种写法可以避免死循环,并符合左端点模板的常规写法。

中点计算采用向下取整 的公式,即 mid = left + (right - left) / 2。当区间长度为偶数时,中点会偏向左边 ,这确保了当 leftright 相邻时,中点指向 left,从而保证区间能够正确收缩,不会出现 right 原地不动的情况。向下取整 是左端点查找的精髓,也是避免死循环的关键。

二、区间缩进规则

在循环中,使用 x = nums[right] 作为基准值(即数组最右边的元素)。根据 nums[mid]x 的比较结果,按照以下规则更新指针:

  • nums[mid] > x :说明 mid 位于前一段(旋转后较大的那一段),因为前一段的所有元素都大于后一段,而最小值在后一段。因此最小值一定在 mid 的右侧,可以安全地将左指针移动到 mid + 1,即舍弃左半区间

  • nums[mid] <= x :说明 mid 位于后一段(包括可能的最小值),因为后一段的所有元素都小于等于最右边元素(且后一段本身升序)。此时最小值可能在 mid 左侧,也可能就是 mid 本身。因此将右指针移动到 mid保留 mid 在搜索区间内 ,继续向左寻找。注意这里不能用 right = mid - 1 ,因为 mid 有可能是最小值,必须保留。

三、循环结束后的处理

循环结束时,leftright 相等,此时指向的位置就是数组中最小值的索引 。直接返回 nums[left] 即可,无需额外判断。无论数组是否经过旋转,这个结果都是正确的。

四、细节注意

  • 基准值 xnums[right] 即数组最右边的元素,它在循环中保持不变(因为 right 会更新,但 x 是初始最右边元素的值)。

  • 循环中比较的是 nums[mid] 与固定的 x,因此需要确保 x 的值在循环过程中始终代表原始最右边元素,这是合理的。

  • 由于数组元素互不相同,不存在相等干扰,比较逻辑清晰。

相关推荐
linx2957 分钟前
第七章 · 标准库容器、算法与 ranges
c语言·开发语言·数据结构·c++·算法
钓鱼的肝11 分钟前
csp-j-s总结(4)
c++·经验分享·笔记·算法
橘子汽水16825 分钟前
Leetcode 763,45 划分字母区间 跳跃游戏II
数据结构·算法·leetcode
别动我齐刘海34 分钟前
ROS2 Jazzy + C++ 实战路线——基础学习1
c++·vscode·python·ubuntu·机器学习·自动驾驶·github
j7~1 小时前
【Linux网络加餐】(篇七)网络版计算器(中):协议落地、报文分隔与完整链路
linux·c++·网络编程·tcp·报文分割·网络计算器·协议落地
天天喝旺仔1 小时前
Git 内部原理深度解析:从 blob/tree/commit 对象到 packfile 与垃圾回收
数据结构·数据库·git·算法·哈希
AIGCmagic社区1 小时前
KITTI AbsRel从6.5压到5.4,Marigold V2用一张32GB卡把编辑DiT收成单步深度估计
人工智能·算法·aigc·ai多模态
青少儿编程课堂1 小时前
多源最短路与最小环(Floyd 算法图论解析)
c++·python·算法·bfs·信息学竞赛
6Hzlia1 小时前
【Classic 150 刷题计划】 LeetCode 228. 汇总区间 | C++ 锚点游标与断点检测法
c++·算法·leetcode
Severus_black1 小时前
【C++初阶】模板初阶——为何C++能和C语言拉开差距?
c++