每日一题——旋转数组的最小数字(II)

旋转数组的最小数字——II

题目链接

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HfqanL7d-1691934029415)(C:\Users\HUASHUO\AppData\Roaming\Typora\typora-user-images\image-20230813204334548.png)]

注:此题是昨天旋转数组的最小数字——I的拓展延伸,昨天题目数组的条件是不会存在重复元素,而本题数组的元素可以重复,因此建议先做前面一题,进行思考,这样求解这一题的过程就会容易理解许多。


旋转数组的性质

旋转数组的最小数字——I我们知道了:

以数组最右侧数据val为参考对象,最小值右侧的数一定小于val,最小值左边的数一定大于val

可以得到以下图像:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1LUqxiEc-1691934029415)(C:/Users/HUASHUO/AppData/Roaming/Typora/typora-user-images/image-20230813204903425.png)]

而今天这一题多了一个条件:数组中可能存在重复元素。我们需要考虑的情况又复杂了一点:

以数组最右侧数据val为参考对象,最小值右侧的数一定等于小于val,最小值左边的数一定大于等于val

我们同样用二分法来解决:

  • 同样,我们设左边界为left,右边界为right,区间的中间下标为mid

  • 如果nums[mid] > nums[right],和原来一样,最小值一定不在左侧区域,left = mid + 1

  • 如果nums[mid] < nums[right],和原来一样,最小值一定不在右侧区域(但可能就是mid位置的值),right = mid

  • 如果nums[mid]==nums[rihgt],这种情况就值得注意了,如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uNuvbIY4-1691934029416)(C:/Users/HUASHUO/AppData/Roaming/Typora/typora-user-images/image-20230813210527291.png)]

  • 此时,nums[mid]可能在最小值的左边,也可能在最小值的右边,因此我们不能随意地舍去mid左侧的数据或者右侧的数据。对于这种情况的处理,我们有两种方法:

方法一:

不能随意舍去mid左半部分或者右半部分是因为最小值到底是在mid的左边还是右边是不确定的,但是如果我们可以通过处理来确定最小值和mid的关系呢?

我们知道,数组旋转后,其被分割成了两串非递减数列,而令我们困扰的就是诸如这种情况:[3,3,1,3],[4,4,5,1,4,4,4,4,4]。那么我们可以删除数组前面所有等于nums[right]的元素,使数组变成诸如[1,3],[5,1,4,4,4,4]的形式,这样当nums[mid]==nums[right]这种情况出现时,mid就一定会出现在最小值的右侧mid右侧的数据也就可以删除了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-unGjeYsq-1691934029416)(C:/Users/HUASHUO/AppData/Roaming/Typora/typora-user-images/image-20230813212421862.png)]

实现代码

int minArray(int* nums, int numsSize){
    int left = 0;
    int right = numsSize - 1;

    //删除数组前面所有等于nums[right]的元素
    while (left < numsSize && nums[left] == nums[right])
        left++;

    while (left < right)
    {
        int mid = (right - left) / 2 + left;

        //如果中间值大于最右边的值,那么最小值一定在中间值的右边
        if (nums[mid] > nums[right])
            left = mid + 1;
        //否则,最小值就在最右边的值的左边,也可能就是这个中间值
        else
            right = mid;
    }

    return nums[right];
}

方法二

虽然我们不能随意舍弃mid的左半部分或者右半部分,但是我们可以确定,无论nums[right]是不是最小值,都有它的一个替代品nums[mid],因此我们可以删除最右边的数据nums[right]

实现代码

int minArray(int* nums, int numsSize){
    int left = 0;
    int right = numsSize - 1;

    while (left < right)
    {
        int mid = (right - left) / 2 + left;

        //如果中间值大于最右边的值,那么最小值一定在中间值的右边
        if (nums[mid] > nums[right])
            left = mid + 1;

        //如果中间值小于最右边的值,那么最小值一定在中间值的左边或者就是中间值
        else if (nums[mid] < nums[right])
            right = mid;

        //如果中间值等于最右边的值,那么就删除最右侧元素
        else    
            right--;
    }

    return nums[left];
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Forward♞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值