剑指offer面试题11:旋转数组的最小数字(Java版已在牛客网AC)

题目

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

思路

看到这道题,最常规的解法是顺序查找,从头到尾遍历一边数组就可以得到最小元素了,这种思路的时间复杂度显然就是O(n)。但是我们仔细阅读题后,可以发现旋转后的数组实际可以划分成两个排序的子数组,而且前面的子数组的元素都大于或者等于后面子数组的元素,同时我们可以发现最小的元素刚好是这两个子数组的分界线。在有序的数组中我们可以使用二分查找法来查找最小的元素,该方法的时间复杂度为O(log n),具体思路如下:

  1. 用两个指针分别指向数组的第一个元素和最后一个元素,按照题目的规则,第一个元素应该是大于或者等于最后一个元素的。
  2. 找到第一个指针和第二个指针中间的元素
  3. 如果该中间元素位于前面的递增数组,则它应该大于或者等于第一个指针指向的元素。此时数组中最小的元素应该位于该中间元素的后面,因此我们把第一个指针指向该中间元素,这样可以缩小查找范围。移动之后第一个指针任然位于前面的递增子数组。
  4. 如果该中间元素小于或者等于第二个指针指向的元素,说明中间元素位后面的递增子数组,此时数组中最小的元素应该位于该中间元素的前面,因此我们将第二个指针指向该中间元素,可以缩小查找范围。移动之后第二个指针任然位于后面的递增子数组。
  5. 用更新后的两个指针重复做上面的操作,直到最后,第一个指针总会指向前面递增子数组中的最后一个元素,第二个指针总会指向后面递增子数组中的第一个元素,此时,第一个指针指向的元素和第二个指针指向的元素相邻,而第二个指针指向的元素刚好是该数组中最小的元素。

以数组{3,4,5,1,2}为例

  1. 首先我们将第一个指针指向第0个元素 3,将第二个指针指向第4个元素 2
  2. 接着找到他两的中间元素 5
  3. 因为5大于第一个指针指向的元素,因此最小元素应该在5的后面,所以将第一个指针指向该中间元素 5
  4. 接着继续找第一个指针和第二个指针的中间元素 1
  5. 因为1 小于第二个指针指向的元素,所以该数组中最小的元素应该在该该中间元素1前面,所以将第二个指针指向该中间元素 1
  6. 此时第一个指针指向的元素刚好和第二个指针指向的元素相邻
  7. 因此判断第二个指针指向的元素 1 为该数组中最小的元素

注意

上述例子属于常规情况,但总会有特殊情况出现,例如

  1. 如果将排序数组的前面的0个元素搬到后面,即排序数组本身任然是一个反转,例如数组{1,2,3,4,5},第一个元素就是最小值
  2. 如果旋转数组为{1,0,1,1,1},是{0,1,1,1,1}的旋转,这种情况下,第一个指针指向的元素等于第二个指针指向的元素等于中间元素,如果继续用上述解决方法则会出现问题,因为此时我们无法判断中间元素是属于前面递增子数组还是属于后面递增子数组。这种情况下就需要对采用顺序查找啦。

代码实现

 publicc int minNumberInRotateArray(int [] array) {
        int index1=0;//指向数组第0个元素
        int index2=array.length-1;//指向数组最后一个元素
        int indexMid=index1;//用来处理第一种特殊情况
        while(array[index1]>=array[index2])
        {
            if(index2-index1==1)//查找结束条件
            {
                indexMid=index2;
                break;
            }
            indexMid=(index1+index2)/2;//找到中间元素的下标
            //用来处理第二种特殊情况
            if(array[index1]==array[index2]&&array[index2]==array[indexMid])
            {
                return minInOrder(array,index1,index2);
            }
            //当中间元素大于等于第一个指针指向的元素
            if(array[indexMid]>=array[index1])
            {
                index1=indexMid;
            }
            //当中间元素小于等于第二个指针指向的元素
            else if(array[indexMid]<=array[index2])
            {
                index2=indexMid;
            }
        }
        return indexMid;
    }
    //第二种特殊情况的处理,就是采用顺序查找,通过遍历数组来查找最小值
    public int minInOrder(int[] array,int index1,int index2)
    {
        int result=array[index1];
        for(int i=index1;i<=index2;i++)
        {
            if(result>array[i])
            {
                result=array[i];
            }
        }
        return result;
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值