题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如{1,2,3,4,5}是{3,4,5,1,2}的一个旋转,该数组的最小值为1。
解题思路:
对于这个已经排序的数组,仔细看其实是由两个排序的数组组成,我们取其中间的那个值就会发现,如果它落在前面那个有序的数组,那么它的值一定会大于该数组的第一个值并且大于后面那个数组所有的值;如果这个值落在后面的那个数组,那么它的值一定小于该数组的末尾的值。最小的元素刚好是这两个子数组的分界线。在排序的数组中我们可以用二分法实现o(logn)d 的查找。
我们用两个指针分别指向数组的第一个元素和最后一个元素。我们可以找到数组中间的元素,如果该中间元素位于前面的递增子数组,那么它应该大于等于第一个指针指向的元素。此时数组中最小的元素应该位于中间元素的后面。我们可以哒第一个指针指向该中间元素。这样可以缩小寻找范围。移动之后的第一个指针仍然位于前面的递增子数组中。同理,如果中间元素位于后面也可以缩小范围。移动之后的指针仍然位于后面的递增子数组之中。指针讲不断更新,做重复的新一轮查找,最终它们会指向两个相邻的元素,而第二个指针指向的刚好是最小的元素,循环结束。
以题上给的数组为例:
最开始:pi指针指向3,p2指向2,在这两个数列中间的是5。我们发现5大于前面的这个数组的第一个,那么可以推断出,我们要找的最小元素不在这个数列中,而是在后面那个数列中,因为是递增数列嘛。然后把第一个指针移动指向这个中间元素5的下标,P2不动
现在是这样,我们发现此时的两个数组中间的元素是1,它也比后面的那个元素的第一个元素小,所以可以判断我们要找的最小元素应该在前面的那一个数列中,可能就是这一个这个中间值。接下来我们移动第二个指针指向这个中间值1的下标。
此时,指针p1和p2指向了两个相邻的元素,则可以判断出1就是最小元素。
源代码:
#include<stdio.h>
int MinInOrder(int *numbers, int head, int tail)
{
int result = numbers[head];
int i;
for(i = head + 1; i <= tail; ++i)
{
if(result > numbers[i])
result = numbers[i];
}
return result;
}
int Min(int *numbers,int length)
{
if(numbers == NULL || length <= 0)
{
printf("invalid imput!\n");
return 0;
}
int head = 0;
int tail = length - 1;
int mid = head;
while(numbers[head] >= numbers[tail])
{
if(tail - head == 1)
{
mid = tail;
break;
}
mid = (head + tail)/2;
if(numbers[head] == numbers[tail] && numbers[mid] == numbers[tail])
{
return MinInOrder(numbers,head,tail);
}
if(numbers[mid] > numbers[head])
{
head = mid;
}
if(numbers[mid] < numbers[tail])
{
tail = mid;
}
}
return numbers[mid];
}
int main(int argc,char *argv[])
{
int a[] = {3,4,5,1,2};
int length = sizeof(a)/sizeof(int);
printf("min = %d\n",Min(a,length));
return 0;
}
运行截图: