首先了解什么是旋转数组:即把一个数组的最开始的若干个元素搬到数组的末尾,即成为旋转数组,例如数组{3,7,1,8,2}为{1,8,2,3,7}的一个旋转数组。
题目:输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素,例如{1,2,3,4,5}数组的一个旋转数组{3,4,5,1,2},其最小的元素为1,
解析:看到题目以后,我们首先可能会想到遍历这个数组,即可找到最小的元素,时间复杂度为o(n),但是这个思路显然没有利用到数组是递增排序的特性,因此我们得继续寻找更优 的方法,因为原本数组是递增排序的,那么旋转之后的数组是由两个递增排序的数组组成,而且前面的子数组的元素都大于或等于后面子数组的元素,并且这个最小的元素正好将两个子数组分隔,因此我们可以试着用二分查找的思路来查找这个最小元素。
与二分查找方法一样,我们可以用两个指针分别指向这个数组的第一个元素和最后一个元素,按照旋转的规则,第一个元素应该是大于或者等于最后一个元素的,(这其实也不完全对,还有一种情况是特例,稍后讨论),
接着我们可以找到数组中间的元素,如果该数组位于前面的递增子数组,那么他应该大于或者等于第一个指针指向的元素,数组的最小元素应该位于中间元素的后面,因此我们可以将指针指向中间元素,这样就缩小了查找的范围,同理如果中间元素位于后一个递增子数组中,那么他应该小于或等于第二个指针指向的元素,此时该数组中最小的元素应该位于该中间元素的前面,因此我们可以将第二个指针指向该中间元素,缩小查找的范围,
按照上面的思路,第一个指针总是指向前面递增数组的元素,第二个指针指向后面递增数组的元素,最终第一个指针指向前面递增数组的最后一个元素,而第二个指针指向后面子数组的第一个元素,而第二个指针指向的刚好是最小的元素,循环结束。
前面提到的第一个元素应该是大于或等于最后一个元素的,特例:如果把排序数组的前0个元素搬到最后面,即排序数组本身,这仍然是数组的旋转,因此第一个数就是最小的数,我们可以直接返回,
还有一种情况存在即数组{1,0,1,1,1}和数组{1,1,1,0,1}都可以看成是递增排序数组{0,1,1,1,1}的旋转,这样的话第一个元素、中间元素、最后一个元素都是1,我们无法判断中间数字1是属于前面的递增子数组还是第二个递增子数组,因此我们就不可以使用而二分查找的思路解决,只能使用顺序查找的方法。
具体代码如下:
<span style="font-size:24px;">//求旋转数组的最小元素 #include<stdio.h> int minorder(int a[],int index,int index2); int min(int *a,int length) { int index,index2,mid; if(a==NULL||length<=0) return 0; index=0; index2=length-1; mid=index; //当将前0个元素搬到后面,即排序数组本身,可以直接返回mid while(a[index]>=a[index2]) { if(index2-index==1) //两个指针指向的元素相邻,即index2指向的元素为最小元素 { mid=index2; break; } mid=(index+index2)/2; if(a[index]==a[index2] && a[mid]==a[index]) //如果不能判断中间元素属于第一个递增元素还是第二个递增元素,则顺序查找 { return minorder(a,index,index2); } if(a[mid]>=a[index]) index=mid; if(a[mid]<=a[index2]) index2=mid; } return a[mid]; } int minorder(int a[],int index,int index2) { int result=a[index]; int i; for(i=index+1;i<=index2;i++) { if(result>a[i]) //顺序查找比较,找到最小的元素 result=a[i]; } return result; } int main() { int a[5]; int i,x; for(i=0;i<5;i++) { scanf("%d",&a[i]); } x=min(a,5); printf("%d\n",x); return 0; }</span>