题目描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
解题思路
思路一:
这是一道二分查找的变形的题目。旋转之后的数组实际上可以划分成两个有序的子数组:前面子数组的大小都大于后面子数组中的元素。注意到实际上最小的元素就是两个子数组的分界线。本题目给出的数组一定程度上是排序的,因此我们试着用二分查找法寻找这个最小的元素。
设置两个指针low,high;low指向数组的开始位置,也就是第一个数组的开始位置,high指向数组的终止位置,也就是第二个数组的结束位置。
(1)如果没有发生旋转,也就是排序数组没有变,这时low指向的数小于high指向的数,数组的首元素就是最小的数,令mid=low;
(2)如果发生旋转,前面的数去了至少一个放在数组的后面,令mid=low+(high-low)/2;
a. 如果中间位置mid的数大于low指向的数,则mid在第一个数组中,让low指向mid所指向的数,low指向的依然是数组1的数;
b. 如果中间位置mid的数小于high所指向的位置,则mid在第二个数组中,high指向mid指向的数,high指向的依然是数组2的数;
c. 如果中间位置的数既等于low位置数,又等于high位置的数,这时候,不能确定移哪个指针,就必须采用顺序查找的方法来寻找最小数;
即:mid不是指向数组1的数,就是指向数组2的数,指向数组1的数,就让low移动到mid的位置,指向数组2就让high移动到mid的位置;直到high移动到数组1的结束的位置,low移动到数组2的开始的位置,此时low与high挨着,而且low所指向的数组2的起始位置中存放的就是最小数。
import java.util.ArrayList; public class Solution { public int minNumberInRotateArray(int [] array) { if(array.length == 0) return 0; int low = 0; int high = array.length - 1; //旋转数组为排序数组本身的特例 int mid = low; while(array[low]>= array[high]){ // 分界点 if(high - low == 1){ mid = high; break; } mid = low + (high - low) / 2; //考虑两侧数字和中间数字相同的特殊情况,采用顺序查找算法查找最小值 if(array[low]== array[high] && array[mid]== array[low]){ return MinInOrder(array,low,high); } // 中间元素位于前面的递增子数组 // 此时最小元素位于中间元素的后面 if(array[mid] >= array[low]){ low = mid; // 中间元素位于后面的递增子数组 // 此时最小元素位于中间元素的前面 }else if(array[mid] <= array[high]){ high = mid; } } return array[mid]; } //顺序查找 private int MinInOrder(int[] array,int low,int high){ int result = array[low]; for(int i = low +1;i<high;i++){ if(result> array[i]){ result = array[i]; } } return result; } }
思路二:
采用二分法解答这个问题mid = low + (high - low)/2
需要考虑三种情况:
(1)array[mid] > array[high]:出现这种情况的array类似[3,4,5,6,0,1,2],此时最小数字一定在mid的右边。low = mid + 1
(2)array[mid] == array[high]:出现这种情况的array类似 [1,0,1,1,1] 或者[1,1,1,0,1],此时最小数字不好判断在mid左边还是右边,这时只好一个一个试 ,high = high - 1
(3)array[mid] < array[high]:出现这种情况的array类似[2,2,3,4,5,6,6],此时最小数字一定就是array[mid]或者在mid的左边。因为右边必然都是递增的。high = mid
注意这里有个坑:如果待查询的范围最后只剩两个数,那么mid 一定会指向下标靠前的数字比如 array = [4,6]array[low] = 4 ;array[mid] = 4 ; array[high] = 6 ;如果high = mid - 1,就会产生错误, 因此high = mid但情形(1)中low = mid + 1就不会错误 import java.util.ArrayList; public class Solution { public int minNumberInRotateArray(int [] array) { if(array == null || array.length == 0) return 0; int low=0; int high=array.length-1; while(low<high){ int mid=low+(high-low)/2; if(array[mid]>array[high]) low=mid+1; else if(array[mid]==array[high]) high=high-1; else high=mid; } return array[low]; } }