[剑指Offer学习] 面试题N6:旋转数组的最小数字

剑指Offer:旋转数组的最小数字

题目描述

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

规律发现

  • 思路:
    旋转之前的数组是一个升序排序的数组,旋转之后的数组可以划分成两个排序的子数组,而且前面的子数组的元素都大于或者等于后面子数组的元素,且最小的元素刚好是这两个子数组的分界线在排序的数组中可以用二分法实现O(nlogn)的查找。二分查找步骤如下:
  • 步骤:
    ① 用两个指针index1和index2分别指向旋转数组的第一个元素和最后一个元素,用indexMid指向index1和index2的中间元素
    ② 如果旋转数组和原数组相同,即旋转元素个数为0,则直接返回index1指向的数字
    ③ 如果index1=index2=index3,则只能通过顺序查找,遍历数组,返回最小元素
    ④ 否则如果indexMid大于index1,则移动index1至indexMid;如果indexMid小于index2,则移动index2至indexMid
    ⑤ 最终index1将指向前面子数组的最后一个元素,index2会指向后面子数组的第一个元素,即他们最后会指向相邻的元素,index2刚好指向最小的元素
  • 注释:需要考虑到旋转数组和原数组相同的情况;需考虑到旋转数组中存在相同元素无法判断中间元素属于前面子数组还是后面子数组

完整代码展示

public class N6旋转数组的最小数字 {
    public static int min(int[] numbers){
        //判断输入的合法性
        if(numbers == null || numbers.length ==0){
            throw new RuntimeException("Invalid input.");
        }

        //指针初始化
        int index1 = 0;
        int index2 = numbers.length-1;
        //初始化为index1指向的元素,一旦发现数组中的第一个数字小于最后一个数字,则直接返回第一个数字
        int indexMid = index1;

        //确保index1在前面子数组的部分,index2在后面子数组的部分
        //否则直接返回index为最小值
        while(numbers[index1] >= numbers[index2]){
            //当处理范围只有两个数据时,返回后一个结果
            if(index2-index1 == 1){
                return numbers[index2];
            }

            //取中间的位置
            indexMid = (index1 + index2) / 2;

            //如果三个数都相等,则需要进行顺序处理,从头到尾找到最小的值
            if(numbers[indexMid] == numbers[index1] && numbers[index2] == numbers[index1]){
                return minInorder(numbers,index1,index2);
            }

            //如果中间位置对应的值在前一个子数组的部分,将index1设置为新的处理位置
            if(numbers[indexMid] >= numbers[index1]){
                index1 = indexMid;
            }

            //如果中间位置对应的值在后一个子数组的部分,将index2设置为新的处理位置
            if(numbers[indexMid] <= numbers[index2]){
                index2 = indexMid;
            }
        }
        //返回最终处理的结果
        return numbers[indexMid];
    }

    //使用顺序遍历的方法找数组中的最小值
    public static int minInorder(int[] numbers,int start,int end){
        int result = numbers[start];
        for(int i = start + 1;i<=end; i++){
            if(result > numbers[i])
                result = numbers[i];
        }
        return result;
    }

    public static void main(String[] args) {
        //典型输入,单调升序数组的一个旋转数组
        int[] a1 = {3,4,5,1,2};
        System.out.println("a1中最小元素" + min(a1));

        //有重复数字,并且重复的刚好是最小的数字
        int[] a2 = {3,4,5,1,1,2};
        System.out.println("a2中最小元素" +min(a2));

        //有重复数字,但重复的数字不是第一个和最后一个数字
        int[] a3 = {3,4,5,1,2,2};
        System.out.println("a3中最小元素" +min(a3));

        //有重复数字,并且重复数字刚好是第一个和最后一个数字
        int[] a4 = {1,0,1,1,1,};
        System.out.println("a4中最小元素" +min(a4));

        //旋转0个元素
        int[] a5 = {1,2,3,4,5};
        System.out.println("a5中最小元素" +min(a5));

        //数组中只有一个元素
        int[] a6 = {2};
        System.out.println("a6中最小元素" +min(a6));

        //数组中数字都相同
        int[] a7 = {1,1,1,1,1,1};
        System.out.println("a7中最小元素" +min(a7));

        //输入NULL
        System.out.println("null中最小元素" +min(null));

    }
}

运行结果显示

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值