【剑指Offer】旋转数组的最小数字

文章讲述了如何在给定一个长度为n的非降序旋转数组中,使用优化的二分查找算法找到最小值,强调了空间复杂度为O(1)和时间复杂度为O(logn)的要求,以及对特殊情况(元素值相等)的处理。
摘要由CSDN通过智能技术生成

1、题目描述

有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。

数据范围:1≤n≤10000,数组中任意元素的值: 0≤val≤10000

要求:空间复杂度:O(1) ,时间复杂度:O(logn)

2、思路分析

如果直接暴力循环求解的话,是行不通的,那样的时间复杂度是O(n)。由原数组是非降序的,所以容易得知旋转后的数组是由两个非降序的子数组组成,左边的子数组的所有元素大于等于右边的子数组的元素,并且两个子数组的分界点就是数组的最小值。所以可以定义一个left=0,right=numsLen-1,当mid的值大于等于left的值时,说明最小值在mid的右边,此时把left放到mid的位置,缩小了范围,并且此时mid任在左边的子数组中。同样,当mid的值小于等于right的值时,说明最小值在mid的左边,此时把right放到mid的位置,缩小了范围,并且此时mid任在右边的子数组中。当left到左子数组的最后一个元素,right到右子数组的第一个元素时,即right-left==1时可以结束循环,此时right的值就是最小值。

此时要特别注意,题目中说的是将“若干个元素搬到数组的末尾”,所以可以是将0个元素搬到数组末尾,即不动数组此时直接将数组第一个值返回即可

                                                                  left和right的图示

此时可以写出代码:

int minNumberInRotateArray(int* nums, int numsLen ) {
    int left = 0,right = numsLen-1,mid = 0;
    //如果说第一个元素小于最后一个元素,说明此时是将数组最开始的0个元素搬到数组末尾
    if(nums[left]<nums[right]) return nums[left];
    while(nums[left]>=nums[right])
    {
        if(right-left==1)
            break;
        mid=(left+right)/2;
        if(nums[mid]>=nums[left]) left=mid;
        else if(nums[mid]<=nums[right]) right=mid;
    }
    return nums[right];
}

3、代码的修正

此时是根据前面的思路写出的代码,但它真的正确吗?

当数组是{0,1,1,1,1}的一个旋转数组{1,0,1,1,1}时根据上面的代码,可以得出的结果是1,很明显不正确。

所以当left和right指向的数组元素的值相同时,需要遍历所有元素来查找,

于是得到最终代码:

int MinInOrder(int* nums,int left,int right)
 {
    int result = nums[left];
    for(int i = left+1;i<=right;i++)
    {
        if(result>nums[i]) result = nums[i];
    }
    return result;
 }
int minNumberInRotateArray(int* nums, int numsLen ) {
    int left = 0,right = numsLen-1,mid = 0;
    //如果说第一个元素小于最后一个元素,说明此时是将数组最开始的0个元素搬到数组末尾
    //所以直接返回第一个即可,注意此时不能有等于,如1,0,1,1,1
    if(nums[left]<nums[right]) return nums[left];
    while(nums[left]>=nums[right])
    {
        if(right-left==1)
            break;
        mid=(left+right)/2;
        //若left,right,mid三者所指向的数相同,则只能顺序查找
        if(nums[left]==nums[mid]&&nums[mid]==nums[right])
            return MinInOrder(nums,left,right);
        if(nums[mid]>=nums[left]) left=mid;
        else if(nums[mid]<=nums[right]) right=mid;
    }
    return nums[right];
}

  • 13
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值