把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0.

发现用二分法解决这个问题很好,{3,4,5,1,2}可以分成两个排好序的子数组{3,4,5}{1,2},而左边的每个数一定大于右边的每个数,所以可以声明两个索引left=0,right=length-1,mid=left(为啥这么设置初始值请看代码注释).
开始循环,循环条件为list[left] >= list[right]。我们要做的事情就是让left一直处于第一个数组里且不断接近第一个数组尾,让right一直处于数组二且不断接近数组二的头。让mid等于(left+right)/2,当mid大于left时,说明此时mid还在第一个数组里,这时就让left=mid;继续循环mid再等于(left+right)/2,假设此时mid小于left了,那么它一定处于第二个数组里了,而且它也小于right(因为right时第二个数组当前最大的).当left+1==right的时候就可以跳出循环了,因为此时right就是我们要找的元素。


 public class Jianzhi{

    public static void main (String[] args){
        int[] num = {3,4,5,1,2};
        int m = find(num ) ;
        System.out.println(m);
    }
    public static int find(int[] list) {
        if(list == null){  //数组为null时
            return -1 ;
        }
        if(list.length == 0 ){  //数组长度为0则返回0
            return 0 ;
        }
        int left = 0 ;    
        int right = list.length - 1 ;
        int mid = left ;    //注意:这一步让mid等于left是有用意的,如果list是排好序的,
                            //那么直接返回list[mid] 
        while(list[left] >= list[right]){ 
            if(left + 1 == right){
                    return right ;
            }
            mid = (left + right) / 2 ;
            if(list[mid] == list[left] && list[mid] == list[right]){
                return minInOrder(list,left,right);
            }
            if(list[mid] >= list[left]){
                left = mid ;
            }
            if(list[mid] <= list[right] ){
                right = mid  ;
            }
        }
        return list[mid] ;
    }
public static int minInOrder(int[] list , int left , int right ){
    int result = list[left] ;
    for(int i = left+1 ; i < right ; i++){
        if(result > list[i] ){
            result = list[i] ;
        }
    }
    return result ;
}

}
 

可能大家比较疑惑为什么有以下这句:

if(list[mid] == list[left] && list[mid] == list[right]){
                return minInOrder(list,left,right);
            }

考虑下面这种情况
图片描述

此时 第一个和第二个索引以及mid索引指向的指都是1,三个数字相同。但我们不能区分出最小的数字在mid的左边还是右边,就没法进行判断了。此时,就不得不采用顺序查找方法:

public static int minInOrder(int[] list , int left , int right ){
        int result = list[left] ;
        for(int i = left+1 ; i < right ; i++){
            if(result > list[i] ){
                result = list[i] ;
            }
        }
        return result ;
    }

整个算法最重要的还是让left和right都往两个数组的公共边界靠拢。

完毕。


Twan
70 声望96 粉丝