JZ11 旋转数组的最小数字

题目描述:有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。

输入: [3,4,5,1,2]

输出:1

思路分析:

  • 数组是非递减的,是有序的数组,那么我们依然可以用二分法,只是变了个形式
  • 定义low、hight、mid,这里的mid = low + (hight-low)/2
  • 三种情况:1,当array[mid] < array[hight],旋转点(最小值)在左侧,旋转的长度超过数组一半的长度,说明mid到hight这段是递增的,那么最小值一定是mid或在mid的左边,hight = mid
  • 2,当array[mid] == array[hight],mid--hight无法判断是否递增,移动hight,缩小范围,hight--
  • 3,当array[mid] > array[hight],旋转点(最小值)在右侧,旋转长度小于数组长度的一半,那么最小值一定在mid的右边,low = mid + 1

代码如下:

 public int minNumberInRotateArray(int[] array) {
        int low = 0;
        int hight = array.length-1 ;
        if(array.length == 0){
            return 0;
        }
        while(low < hight){
            int mid = low + (hight - low) / 2;
            if(array[mid] < array[hight]){ //最小值在左侧,递增的,往左继续查找
                hight = mid;//注意循环的判断条件
            }else if(array[mid] == array[hight]){//mid--hight无法判断是否递增,移动high缩小范围
                hight--;
            }else if(array[mid] > array[hight]){//最小值在右侧,往右继续查找
                low = mid + 1;
            }
        }
        return array[low];//low=high时退出循环
}

附学习知识:

在一个旋转有序数组中,如何使用二分查找法查找一个整数

先复习一下二分查找算法

描述:

1

注意这里有一个前提:我们并不直接知道给定数组的旋转点。

在分析之前,首先明确一个概念:旋转点

旋转点是什么呢?我们这里规定,假设旋转有序数组恢复为普通有序数组,位于普通有序数组第一个位置的元素,就是旋转数组的旋转点。

直白地说,旋转点就是旋转数组中最小的元素

1

那么,当我们选择中位数,进行一次二分查找的时候,会出现哪些结果呢?仅仅从中位数与旋转点的相对位置来看,有两种结果:

情况A,旋转点在中位数的右侧

1

这种情况下有两个特点:

1.中位数以及它左侧的元素,全部是升序的

2.最左侧元素,必定小于等于中位数。

情况B,旋转点在中位数的左侧,或与中位数重合

1

这种情况下有两个特点:

1.中位数以及它右侧的元素,全部是升序的

2.最左侧元素,必定大于中位数。

上面所分析的,仅仅是从中位数与旋转点的相对位置角度。如果再引入要查找的目标整数呢?上面的情况A和情况B,就会各自分为两种子情况。

首先回过头看看上述的情况A,要查找的目标整数(假设存在)有可能出现在哪里呢?

答案很简单:

1.查找目标在中位数的左侧

1

由于情况A的中位数左侧是升序区,所以查找目标出现在左侧的条件是:

最左侧元素 <= 查找目标 < 中位数

2.查找目标在中位数的右侧

1

由于查找目标出现在左侧的条件已经确定,那么出现在右侧的条件判断就简单了:

!(最左侧元素 <= 查找目标 < 中位数)

接下来我们再看看上述的情况B(旋转点在中位数的左侧,或与中位数重合)要查找的目标整数(假设存在)可能出现在哪里呢

答案也是同样道理:

1.查找目标在中位数的右侧

由于情况B的中位数右侧是升序区,所以查找目标出现在右侧的条件是:

中位数 < 查找目标 <= 最右侧元素

2.查找目标在中位数的左侧

由于查找目标出现在右侧的条件已经确定,那么出现在左侧的条件判断就简单了:

!(中位数 < 查找目标 <= 最右侧元素)

综上,我们总结了旋转数组二分查找可能出现的四种情况

注意:如果如果二分查找选取的中位数正好是要查找的目标元素,后面旋转点和和中位数的位置关系就不用考虑了,最后将五种情况归纳为一个树形图:

最后代码如下:

public static int rotatedBinarySearch(int[] array, int target){	
    int start = 0, end = array.length-1;	
    while(start<=end)	
    {	
        int mid = start + (end-start)/2;	
        if(array[mid]==target){	
            return mid;	
        }	
        //情况A:旋转点在中位数右侧(旋转的数组长度小于数组长度的一半)
        if(array[mid]>=array[start])	
        {	
            //最左侧元素 <= 查找目标 < 中位数	
            if(array[mid]>target && array[start]<=target){	
                end = mid - 1;	
            } else {	
                start = mid + 1;	
            }	
        }	
        //情况B:旋转点在中位数左侧,或与中位数重合(旋转的数组长度大于数组长度的一半)	
        else {	
            //中位数 < 查找目标 <= 最右侧元素	
            if(array[mid]<target && target<=array[end]){	
                start = mid + 1;	
            } else {	
                end = mid - 1;	
            }	
        }	
    }	
    return -1;	
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值