参考自:《剑指Offer——名企面试官精讲典型编程题》
题目:旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如数组 {3, 4, 5, 1, 2}为{1, 2, 3, 4, 5}的一个旋转,该数组的最小值为1。
主要思路:旋转后的数组分成两个排序的子数组,前面的子数组都大于或等于后面的子数组,最小元素就是两个子数组的分界线。因此,使用两个指针(左指针和右指针)分别指向第一个元素和最后一个元素,接着找到数组的中间元素((左指针+右指针)/2),判断其与两个指针指向元素的大小。
分三种情况:
1.若中间元素大于或等于左指针指向的元素:说明中间元素位于前面的非递减子数组,最小元素应该位于该中间元素的后面,因此把左指针指向中间元素,缩小查找范围。
2.若中间元素小于或等于右指针指向的元素:说明中间元素位于后面的非递减子数组,最小元素应该位于该中间元素的前面,因此把右指针指向中间元素,缩小查找范围。
1和2情况中要移动指针,直到左指针指向前面子数组的最后一个元素,右指针指向后面子数组的第一个元素(该元素即为最小值)。
3.若中间元素同时等于左指针和右指针指向的元素,则只能按顺序查找。
关键点:数组部分有序,缩小查找范围,二分查找
时间复杂度:O(logn)
public class MinNumberInRotateArray {
public static void main(String[] args) {
int[] array1 = {3, 4, 5, 1, 2};
int[] array4 = {1, 0, 1, 1, 1};
System.out.println(findMinNumberInArray(array1));
System.out.println(findMinNumberInArray(array4));
}
private static int findMinNumberInArray(int[] array) {
if (array == null || array.length <= 0) {
return -1;
}
//左指针
int leftIndex = 0;
int rightIndex = array.length - 1;
int midIndex = leftIndex;
while (array[leftIndex] >= array[rightIndex]) {
//左右指针相邻,找到最小值
if (rightIndex - leftIndex == 1) {
midIndex = rightIndex;
break;
}
midIndex = (leftIndex + rightIndex) / 2;
//前中后三个值相等,则顺序查找
if ((array[leftIndex] == array[rightIndex])
&& (array[leftIndex] == array[midIndex])) {
return minInOrder(array, leftIndex, rightIndex);
}
//中间值大于或等于左指针指向元素(前半段还是非递减),则最小值在后半段
if (array[midIndex] >= array[leftIndex]) {
leftIndex = midIndex;
}
//中间值小于或等于右指针指向元素(后半段还是非递减),则最小值在前半段
else if (array[midIndex] <= array[rightIndex]) {
rightIndex = midIndex;
}
}
return array[midIndex];
}
/**
* 顺序查找最小值
*
* @param array the array
* @param aheadIndex the ahead index
* @param behindIndex the behind index
* @return the int
*/
private static int minInOrder(int[] array, int aheadIndex, int behindIndex) {
int result = array[aheadIndex];
for (int i = aheadIndex + 1; i <= behindIndex; i++) {
if (result > array[i]) {
result = array[i];
}
}
return result;
}
}