寻找旋转数组的最小值

需求:

    假设一个旋转排序的数组其起始位置是未知的(比如0 1 2 4 5 6 7 可能变成是4 5 6 7 0 1 2)。你需要找到其中最小的元素。数组中有可能包含重复元素。

分析:

    1、旋转的概念

    把一个数组最开始的若干元素搬到数组的末尾,叫做数组的旋转。如果是排序数组的旋转,那么旋转之后的数组有可能是排序的,也有可能包含两个有序子数组,左边的有序数组中元素都大于等于右边的有序数组中的元素。

    2、简易解法

    1)对数组进行排序,返回角标0的元素

    2)利用集合的Collections.min(List)方法求最小值

    3)从前向后遍历数组,如果当前元素大于后面的元素,那么后面的元素即为最小值

    3、二分法

    传统的二分法都是应用于排序数组,而旋转数组在一定程度上也是有序的,因此可以试图按照二分法的思想求解最小值。求数组的最小值,就是求左右两个子数组的临界位置,因此可以设置两个变量,index1和index2,分别用来指示左右两子数组中的元素,当两个变量差值是1的时候,说明第二个变量指示的值是最小值。当两个变量差值大于1,那么求中间值mid,如果中间值大于等于index1指示的值,那么说明中间值在左子数组中,更新index1为中间值所在的角标,否则,更新index2为中间值所在角标。

    有个特殊情况,当index1、index2和mid值相同,比如11101和10111,如果index1=0,index2=4,两个数组中中间值都是1,这时三个值相同,如果我们认为中间值大于等于index1对应的值,即中间值在左子数组中,那么对于第二个旋转数组是不对的。所以这种情况,只能顺序查找。

代码:

public class Solution {
    /*
     * @param nums: a rotated sorted array
     * @return: the minimum number in the array
     */
    public int findMin(int[] nums) {
        // write your code here
        /*
        //思路一:借助数组排序来做
        if(nums == null || nums.length <= 0){
            //抛出的异常表明向方法传递了一个不合法或不正确的参数。 
            throw new IllegalArgumentException("invalid parameters");
        }
        
        Arrays.sort(nums);
        return nums[0];
        */
        /*
        //思路二:从前向后遍历,找到第一个比左边小的元素,即为最小,如果找不到,就返回第一个元素
        if(nums == null || nums.length <= 0){
            throw new IllegalArgumentException("invalid parameters");
        }
        
        for(int i = 0; i < nums.length-1; i++){
            if(nums[i] > nums[i+1]){
                return nums[i+1];
            }
        }
        
        return nums[0];
        */
        /*
        //思路三:借助集合的Collections.min()来做
        if(nums == null || nums.length <= 0){
            throw new IllegalArgumentException("invalid parameters");
        }
        
        List<Integer> list = new ArrayList<Integer>();
        for(int i = 0; i < nums.length; i++){
            list.add(nums[i]);
        }
        
        return Collections.min(list);
        */
        /*
        //思路四:初始化最小值,遍历求最小值
        if(nums == null || nums.length <= 0){
            throw new IllegalArgumentException("invalid parameters");
        }
        
        int min = nums[0];
        for(int i = 1; i < nums.length; i++){
            if(nums[i] < min){
                min = nums[i];
            }
        }
        
        return min;
        */
        //二分法
        if(nums == null || nums.length <= 0){
            throw new IllegalArgumentException("invalid parameters");
        }
        
        //定义两个指针,指向数组的头和尾
        int index1 = 0, index2 = nums.length-1;
        //定义指针,指向中间值
        int indexMid = 0;//初始化为0
    
         //当第一个指针指向的元素大于等于第二个指针指向的元素,说明两个指针分别在两个子数组中
         while(nums[index1] >= nums[index2]){
             //当两个指针指向的元素是相邻的,那么第二个指针对应的元素是最小值
             if(index2-index1 == 1){
                 indexMid = index2;
                 break;
             }
             
             //当两个指针不是相邻的,需要求中间值,然后和两指针元素比较大小
             indexMid = (index1 + index2) / 2;
             
             //当三个指针元素相同,那么需要顺序查找
             if(nums[index1] == nums[index2] && nums[index1] == nums[indexMid]){
                 return minInorder(nums, index1, index2);
             }
             
             //当中间值大于第一个指针元素,说明中间值在第一个子数组中
             if(nums[indexMid] >= nums[index1]){
                 index1 = indexMid;
             }
             else /*if(nums[indexMid] <= nums[index2])*/{
                 index2 = indexMid;
             }
         }
         
         return nums[indexMid];
    }
    
    //求arr数组中角标index1到index2之间的最小值
    public int minInorder(int[] arr, int index1, int index2){
        int min = arr[index1];
        
        for(int i = index1+1; i <= index2; i++){
            if(arr[i] < min){
                min = arr[i];
            }
        }
        
        return min;
    }
}

拓展:

如果没有重复元素,那么不需要进行index1、index2、indexMid元素的比较。

代码:

public class Solution {
    /*
     * @param nums: a rotated sorted array
     * @return: the minimum number in the array
     */
    public int findMin(int[] nums) {
        // write your code here
         if(nums == null || nums.length <= 0){
             throw new IllegalArgumentException("invalid parameters");
         }
         
         int index1 = 0, index2 = nums.length-1, indexMid = 0;
         while(nums[index1] > nums[index2]){
             if(index2-index1 == 1){
                 indexMid = index2;
                 break;
             }
             
             indexMid = (index1+index2)/2;
             if(nums[indexMid] > nums[index1]){
                 index1 = indexMid;
             }
             else{
                 index2 = indexMid;
             }
         }
         
         return nums[indexMid];
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值