剑指offer第二版面试题11:旋转数组的最小数字(JAVA版)

题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。

解题思路:
1、暴力解法,从头到尾遍历一次,我们就能找出最小的元素,复杂度为O(n),但是没有利用输入的旋转数组的特性,肯定达不到面试官的要求。
2、二分查找,这里的数组可以看成两个有序的子数组,二分查找对有序数组非常有效,复杂度为O(logn)。

具体分析:
1、先分析数组的特点,这两个有序的数组,前一个数组的所有值都大于等于后一个数组的值,最小值应该出现在后一个数组的第一个元素上。
2、二分查找缩小查找范围依赖于中间值和待查找值的比较。 设置两个指针,index1指向第一个元素,index2指向最后一个元素。 如果中间indexMid指向的元素大于或者等于index1指向的元素,那么,最小的元素一定在indexMid之后。如果indexMid指向的元素小于或者等于index2指向的元素,那么最小元素一定在indexMid之前或者就是indexMid所指向的元素。 就是利用这种判断条件来不断缩小查找范围。
3、结束条件:
最终index1会指向前一个子数组的最后一个元素,index2会指向后一个子数组的第一个元素,两者之间的距离为1。而index2指向的就是最小的元素。
4、特殊情况:
如果被旋转的个数为0,也就是只有一个升序的数组,这时候没有必要去查找,因为第一个元素就是最小的元素。
还有在查找中经常遇到的一种情况,就是有相同元素的情况。 比如 {1, 0, 1, 1, 1} 是{0, 1, 1, 1, 1}的一个旋转。index1、index2、indexMid指向的元素值都为1,这时候不能判断最小值所在的范围,所以需要只能顺序遍历。

代码如下:

public class Solution {
    public static Integer min(int[] array){
        if (array == null || array.length == 0) {
            return null;
        }
        
        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)/2;
            //如果high,low,mid指向同一个元素,则该数组中右重复的数字,则只能顺序查找
            if (array[mid] >= array[low]) {
                low = mid;
            } else if (array[mid] <= array[high]) {
                high = mid;
            }
        }
        return array[mid];
    }
    //顺序查找
    public static int minInOrder(int[] array) {
        int min = array[0];
        for (int i = 1; i < array.length; i++) {
            if (array[i] < min) {
                min = array[i];
            }
        }
        return min;
    }
    public static void main(String[] args) {
        int[] array = {3,4,5,1,2};
        Integer result = min(array);
        System.out.println(result);
    }
}
原文地址:https://www.cnblogs.com/xhlwjy/p/11259566.html