旋转数组的最小数字(剑指offer)

题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称为数组的旋转.输入一个递增排序的数组的一个旋

转,输出旋转数组的最小元素.例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的

最小值为1.


  这道题的直观解法并不难,从头到尾遍历数组一次,我们就能找出最小的元素.这种思路的时间复杂度显然是

O(N).但是这个思路没有利用输入旋转数组的特性,肯定达不到面试官的要求.

  我们注意到旋转之后的数组实际上可以划分为两个排序的子数组,而且前面的子数组的元素都大于或者等于后面

子数组的元素.我们还注意到最小的元素刚好是这两个子数组的分界线.在排序的过程中我们可以用二分查找实现

O(log n)的查找:找中间元素,让其跟元素首元素比较,如果大于首元素,则中间元素属于前半段有序数

组,如果小于尾元素,那么中间元素就是后半段的元素。依次查找


  这样,和二分查找一样,我们用两个指针分别指向数组的第一个元素和最后一个元素.按照题目中的旋转的规\

则,第一个元素应该是大于或者等于最后一个元素(这其实并不完全对,对于特例,后面加以讨论)

  接着我们可以找到数组中间的元素.如果该中间元素位于前面的递增子数组,那么它应该大于或者等于第一个指

针指向的元素.此时数组中最小的元素应该位于该中间元素的后面.我们可以把第一个指针指向该中间元素,这样可

以缩小寻找的范围.移动之后第一个指针仍然位于前面的递增子数组之中.

  同样的,如果中间元素位于后面的递增子数组,那么它应该小于或者等于第二个指针指向的元素.此时该数组中

最小的元素应该位于该中间元素的前面.我们可以把第二个指针指向该中间元素,这样也可以缩小寻找的范围.移动

之后的第二个指针仍然位于后面的递增子数组之中.

  不管移动第一个指针还是移动第二个指针,查找范围都会缩小到原来的一半.依次


  按照上述思路,第一个指针总是指向前面递增数组的元素,而第二个指针总是指向后面递增数组的元素.最终第

一个指针指向前面数组的最后一个元素,而第二个指针指向后面子数组的第一个元素,也就算它们最终会指向两个相

邻的元素.而第二个指针也就刚好指向最小的元素.哈哈,循环结束.....


  比如前面的数字:

  

  此时两个指针的距离为1,表面第一个指针已经开始指向了第一个递增子数组的末尾,而第二个指针指向第二个

递增数组的开头.第二个字数组的第一个数字就是最小的数字,因此第二个指针指向的数字就是我们要查找的结果

 

#include <stdio.h>

int Min(int *numbers, int length)
{
        if(numbers == NULL || length <= 0)
                return -1;
        int low = 0;
        int high = length -1;

        int middle;
        while(numbers[low] >= numbers[high])
        {
                if(high - low == 1)
                {
                        middle = high;
                        break;
                }
                middle = (low + high)/2;
                if(numbers[middle] >= numbers[low])
                        low = middle;
                else if(numbers[middle] <= numbers[high])
                        high = middle;
        }
        return numbers[middle];
}

int main()
{
        int numbers[] = {2,3,4,5,1};
        int length = sizeof(numbers)/sizeof(int);

        printf("%d\n",Min(numbers, length));
}
运行结果:



   前面我们提到在旋转数组中,由于是把递增排序数组前面的若干个数字搬到数组的后面,因此第一个数字总是

大于或者等于最后一个数字.但是,按照定义还有的一个特例:

如果把排序数组的前面的0个元素搬到最后面,即排序数组本身,这仍然是数组的一个逆转,我们的代码需要支持这

种情况.此时,数组中的第一个数字就算最小的数字,可以直接返回.因为数组中的第一个数字跟最后一个比较,如

果第一个小,那么就说明该数组是排序的,可以直接返回第一个数字了.

   值得注意的是:当两个数相同的时候,并且中间的数字也相同的时候,我们把low移动到了middle的

位置,然后,我们也就理所应当的认为此时最小的数字位于中间数字的后面.是不是一定这样的呢???

   

从上图可以看出:第一个指针和第二个指针都是1,且中间的数字也是1

也就是说:最小值可能在左边,也可能在右边


当第一个和最后一个值相等的时候,中间的值或者为最小的,或者跟它们两个一样

如果:中间值小,那么就是我们要求的;如果中间值不小的话,那么我们就无法通过移动两个指针来缩小查找的范围

.我们就必须用顺序查找的方法了....

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值