LintCode-寻找旋转排序数组中的最小值

问题:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转,输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素,例如数组{3,4,5,1,2}为数组{1,2,3,4,5}的一个旋转,该数组的最小值为1。

解题思路:一个递增数组经旋转后分为两部分,它们都是递增的数组,第一个子数组的第一个元素不小于第二个子数组的最后一个元素,我们要寻找的是第一个子数组最后一个元素的下一个元素或第二个子数组的第一个元素。利用二分查找法,设有两个指针分别指向旋转数组的第一个和最后一个元素,设为left和right,找到两个指针的中间元素mid,如果它不小于第一个元素,则说明这个中间元素位于第一个递增子序列,我们要找的元素在这个中间元素的后面,此时将指向第一个元素指针left置为mid;如果它不大于最后一个元素,则说明这个中间元素位于第二个递增子序列,我们要找的元素在这个中间元素的前面,此时将指向最后一个元素的指针right置为mid。接下来递归调用该函数,最后left将指向第一个递增子序列的最后一个元素,right将指向第二个递增子序列的第一个元素,也即我们要寻找的元素,此时right-left==1。

根据上述解题思路写出的代码如下:

int Min(int* numbers,int length){
  int left = 0;
  int right = length-1;
  while(right-left>1){
    int mid = (left+right)/2;
    if(numbers[mid] >= numbers[left])
      left = mid;
    if(numbers[mid] <= numbers[right])
      right = mid;
  }
  return numbers[right];
}

但是上述写出的代码在某些情况下会遇到问题,如果该旋转数组是由原递增数组将最开始的0个元素移到数组末尾,那么将出现问题,如{1,2,3,4,5}找到的将是元素3。问题在于此时第一个子数组的第一个元素小于第二个子数组的最后一个元素,所以我们要对代码进行修改,修改后的代码如下:

int Min(int* numbers,int length){
  int left = 0;
  int right = length-1;
  int mid = left;
  while(numbers[left] >= numbers[right]){
    if(right-left == 1){
      mid = right;
      break;
    }
    int mid = (left+right)/2;
    if(numbers[mid] >= numbers[left])
      left = mid;
    if(numbers[mid] <= numbers[right])
      right = mid;
  }
  //注意此时返回的是最中间元素的值,而不是numbers[right],是为了兼容第一个元素时最小值的情况
  return numbers[mid];
}

继续寻找特殊情况,我们发现如果是类似于如下的数组{1,0,1,1,1}或{1,1,1,0,1},第一个元素、最后一个元素、中间元素均相等,此时我们无法判断中间的元素时位于第一递增子序列({1,1,1,0,1})还是第二递增子序列({1,0,1,1,1}),此时我们只能遍历该序列从中找到最小值。修改后的代码如下:

int Min(int* numbers,int length){
  int left = 0;
  int right = length-1;
  int mid = left;
  while(numbers[left] >= numbers[right]){
    if(right-left == 1){
      mid = right;
      break;
    }
    int mid = (left+right)/2;
    if(numbers[mid] == numbers[left] && numbers[mid] == numbers[right]){
      return finMin(numbers,left,right);
    }
    if(numbers[mid] >= numbers[left])
      left = mid;
    if(numbers[mid] <= numbers[right])
      right = mid;
  }
  return numbers[mid];
}
int findMid(int* numbers,int left,int right){
  int min = numbers[left];
  for(int i = left+1; i <= right; i++){
    if(numbers[i] < min)
      min = numbers[i];
  }
  return min;
}
</pre><pre name="code" class="cpp" style="border: 1px solid rgb(255, 255, 204); font-family: 'Courier New'; overflow: auto; font-size: 16px; line-height: 24px; background-color: rgb(255, 255, 252);">

在这里我找到了另外一个代码,对上面特殊情况进行了修正

<pre name="code" class="cpp">class Solution {
public:
    /**
     * @param num: a rotated sorted array
     * @return: the minimum number in the array
     */
    int findMin(vector<int> &num) {
        // write your code here
        int l = 0;
        int r = num.size()-1;
        if(num[l]<num[r])
            return num[l];
        while(l<r)
        {
            int mid = (l+r)/2;
            if(num[mid]>num[r])
                l = mid+1;
            else
                r = mid;
        }
        return num[l];
    }
};



                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值