剑指Offer——旋转数组的最小数字

题目:把一个数组的最开始几个元素搬到数组的末尾,叫做数组的旋转。输入一个递增数组的旋转,找出其最小元素,例如数组A{3,4,5,1,2}是数组B{1,2,3,4,5}的一个旋转,输入数组A后,输出最小元素应该为1

此题最直观的解法,从头到尾遍历数组,当第N个元素比第N-1个元素小时,第N个元素即为数组的最小元素。此法所需时间复杂度为O(N)

注意到在有序数组中查找某个数字的时候是可以用二分查找将时间复杂度降为O(LogN)的,本题也在一定程度上属于有序数组。

在这里插入图片描述

如上图,首先声明两个指针P1,P2分别指向数组的首尾元素3和2,以及PMid指针指向P1和P2的中间元素5。
比较PMid和P2指向元素的大小,如果PMid的元素较大,则表明数组的最小元素一定在PMid的右边,将P1放到PMid的位置。否则的话最小元素一定在PMid的左边(也有可能是PMid自己),将P2放到PMid的位置。

此处情况为前者,所以将P1放到PMid的位置(图b)
之后找到PMid元素为1,比P2的元素小,所以数组最小值一定在PMid的左边或者是它本身。但是此时它的左边为P1,已经没有元素了,因此最小元素为其自身。

似乎已经能够找到解决办法了,但是还可以再关注一些特殊情况。
当输入数组为空指针,自然不应该返回有效值,此时可以报错或者返回一个-9999的无效值。
当输入数组只有1个元素,直接返回该数组即可。
当数组没有旋转,也算是一个有效输入,此时应该直接返回第一个元素即可。当没有旋转时,指针P1应该比P2小,所以在程序一开始时即可判断P1P2大小来确定数组有没有旋转。
在这里插入图片描述
最后是当数组有多个元素相同时,如上图这种情况的话,P1、P2和PMid将是完全一样,此时无法判断数组的最小元素位于何处。因此如果碰到这种情况的话只能通过顺序查找来处理。

代码如下:

int MinInOrder(int Array[], int P1, int P2){
  int result= Array[P1];
  for(int i = P1; i < P2; ++i){
    if(Array[i] < result) result = Array[i];
  }
  return result;
}

int MinNumberInRotatedArray(int Array[], int Len){
  if(Array==nullptr){
    printf("Array is empty!\n");
    return -9999;
  }

  if(Len==1) return Array[0];

  int P1 = 0;
  int P2 = Len - 1;

  if(Array[P1] < Array[P2]) return Array[P1]; //数组并没有旋转,返回第一个元素

  int PMid = (P1 + P2)/2;

  while(true){
    if(Array[P1]==Array[P2]&&Array[P1]==Array[PMid])
      return MinInOrder(Array,P1,P2);//如果三者相等,则只能用顺序查找

    if(P2 - P1==1) return Array[P2];//如果两者已经挨在一起,则右指针是数组头

    if(Array[PMid] > Array[P2]) {
      P1 = PMid;
      PMid = (P1 + P2)/2;
    }
    else{
      P2 = PMid;
      PMid = (P1 + P2)/2;
    }
  }
}

本题代码在这里

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值