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

array表示递增排序旋转数组,使用left和right指针分别代表数组的左边界和右边界,数组中间元素的下标mid = (left+right)/2。

一个标准的递增排序旋转数组,总是有array[left] >= array[right],当array[left] < array[mid]时,说明最小元素在mid右侧,使left = mid;array[left] > array[mid]时,最小元素在mid左侧,使right = mid。直到left和right相邻时,array[right]即是最小元素。。但有以下两种特殊情况:

第一种,即数组旋转后跟没有旋转一样,即array[left] < array[right],此时array[left]为最小元素。

第二种,array[left] == array[right] == array[mid],此时最小元素可能在mid左侧,也可能在右侧。因此,此时只能遍历left和right之间的数据找出最小元素。

代码如下:

public class Min {
    /**
     *
     * @param array 一个递增排序数组的旋转
     * @param length 数组长度
     * @return 返回最小值
     */
    private static int min(int[] array,int length){
        if (array == null || length == 0){
            throw new NullPointerException("array is empty");
        }
        int left = 0;
        int right = length - 1;
        //第一个元素大于最后一个元素,说明该数组为旋转的
        while (array[left] >= array[right]){
            //前后指针相邻,即array[right]是最小元素
            if (right - left == 1){
                return array[right];
            }
            int mid = (left + right)/2;

            //中间元素==左边界元素==右边界元素,无法确定最小元素在中间元素的左侧还是右侧,只能通过遍历求得最小元素
            if (array[left] == array[mid] && array[left] == array[right]){
                return minInOrder(array,left,right);
            }
            //中间元素大于左边元素,最小元素在中间元素的右侧,否则最小元素在中间元素左侧
            if (array[mid] > array[left]){
                left = mid;
            }else {
                right = mid;
            }
        }
        //第一个元素小于最后一个元素,说明该数组非旋转的,因此直接返回第一个元素
        return array[left];
    }

    /**
     *
     * @param array 一个递增排序数组的旋转
     * @param left 左边界
     * @param right 右边界
     * @return 返回最小值
     */
    private static int minInOrder(int[] array,int left,int right){
        int result = array[left];
        for (int i = left+1;i < right;++i){
            if (result > array[i]){
                result = array[i];
            }
        }
        return result;
    }

    public static void main(String[] args) {
        int[] a = {3,4,5,1,2};
        System.out.println(min(a,a.length));

        int[] b = {5,1,2,3,4};
        System.out.println(min(b,b.length));

        int[] c = {1,2,3,4,5};
        System.out.println(min(c,c.length));

        int[] d = {3,4,5,6,2,3};
        System.out.println(min(d,d.length));

        int[] e = {4,4,4,3,4};
        System.out.println(min(e,e.length));

        int[] f = {4,1,2,3,4,4,4};
        System.out.println(min(f,f.length));

        int[] g = {1,0,1,1,1};
        System.out.println(min(g,g.length));

        int[] h = {1};
        System.out.println(min(h,h.length));
    }
}

在没有加入第二种情况的代码(23-25行)时,输入数组e,输出的结果为4;输入数组h,程序陷入死循环。而输入数组g,输出的结果为0是正确的。这三个数组在查找的过程中都存在array[left] == array[right] == array[mid]的情况。