剑指offer:旋转数组的最小数字
题目
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
解题思路
这是一道二分查找的变形题,这里的旋转数组是两个有序的递增子数组拼接起来的,且前面一个子数组里的任意一个数都大于后面子数组中的数。而要找的最小值就是前后两个子数组的分界点。
用三个指针begin、end、middle分别指向数组的首尾和中间元素
1)若middle元素大于或者等于begin元素说明中间元素位于前半部分数组中,最小元素在后半部分的数组中,则将begin设为middle继续在后半部分中寻找
2)若middle元素小于或者等于end元素说明中间元素位于后半部分数组中,最小部分在前半部分或者中间元素就是最小元素,则将end设为middle继续在前半部分中寻找
这样不断缩小范围,直到最后begin和end会成为相邻的元素,此时end所指元素就是最小值
特殊情况:
1)0个元素旋转到后半部分,那么此时旋转数组就是一个非递减数组,最小值就是第一个元素,也就是为什么初始middle设为0的原因
2)当begin、end、middle三个元素都相等时,无法判断中间元素在前半部分数组还是后半部分数组,只能遍历数组寻找最小值
例如1,1,1,0,1和1,0,1,1,1都可以看成是0,1,1,1,1的翻转数组
代码
1 public int minNumberInRotateArray(int [] array) { 2 if(array.length==0) 3 return 0; 4 int begin = 0,end = array.length-1,middle = 0; 5 while(array[begin]>=array[end]){ 6 if((end-begin)==1){ 7 middle = end; 8 break; 9 } 10 middle = (begin+end)/2; 11 if(array[begin]==array[middle]&&array[middle]==array[end]){ 12 return minInOrder(array,begin,end); 13 } 14 if(array[middle]>=array[begin]){ 15 begin = middle; 16 }else if(array[middle]<=array[end]){ 17 end = middle; 18 } 19 20 } 21 return array[middle]; 22 } 23 24 private int minInOrder(int[] array,int begin,int end){ 25 int min = array[begin]; 26 for(int i = begin+1;i<end;i++){ 27 if(min>array[i]){ 28 min = array[i]; 29 } 30 } 31 return min; 32 }
keep positive and calm,then go on