【剑指Offer】个人学习笔记_11_旋转数组的最小数字

刷题日期:19:3429 星期一2021年3月22日

个人刷题记录,代码收集,来源皆为leetcode

经过多方讨论和请教,现在打算往Java方向发力

主要答题语言为Java

题目:

  • 剑指 Offer 11. 旋转数组的最小数字

    难度简单265

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

    示例 1:

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

    示例 2:

    输入:[2,2,2,0,1]
    输出:0
    

题目分析

如果只要求输入输出的话,那么就是排序问题,找到最小值即可,但是这样就忽略了旋转数组的特性。

根据题目中旋转数组的特性,只要遍历数组中的元素,第一个大于后一个元素的那个数,即为最小的数字。

初始解答:

按照初步的设想写出了如下代码,但是连最基本的都没有跑通。

class Solution {
    public int minArray(int[] numbers) {
        int i;
        for(i = 0;i<numbers.length-1;i++) {
            if(numbers[i] > numbers[i+1]) break;
        }
        return numbers[i+1];
    }
}

java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
at line 7, Solution.minArray
at line 57, DriverSolution.helper
at line 82, Driver.main

改用while循环,一样报错

class Solution {
    public int minArray(int[] numbers) {
        int i = 0;
        while(numbers[i] <= numbers[i+1]) i++;
        return numbers[i+1];
    }
}

不懂为什么错,参考方法一,修改了循环的逻辑,然后就能跑出示例了。

class Solution {
    public int minArray(int[] numbers) {
        int i = 0;
        while(numbers[i] > numbers[i+1]) i++;
        return numbers[i];
    }
}

在提交过程中遇到更多边界条件,比如数组为1,数组为几个相同的数字,都出现了问题,没有考虑周全。

class Solution {
    public int minArray(int[] numbers) {
        int i = 0;
        if(numbers.length == 1) return numbers[0];
        while(numbers[i] >= numbers[i+1]) i++;
        return numbers[i];
    }
}

思绪很乱,赶紧用while就是解决不了问题,能确定的条件不够满足所有的边界输入。

对比方法四,可以发现是自己把while里面的情况没有很好的分清楚,以及想把所有的边界输入都用一句语言包括到,所以没能实现。

最后还是采用了方法一的写法

class Solution {
    public int minArray(int[] numbers) {
        int n = numbers.length;
        for(int i = 1;i < n; i ++) {
            if(numbers[i] < numbers[i-1]) return numbers[i];
        }
        return numbers[0];
    }
}

执行结果:

通过

显示详情

执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户

内存消耗:38.2 MB, 在所有 Java 提交中击败了66.48%的用户

对比方法一,方法二,我认为是自己对两种return包含所有输入的性质没有思考清楚,只能想到在循环中返回一次,想不到注释中的前两种情况。

还有参考书上采用二分法的方式解决问题,时间复杂度会更低。O(logn)

看评论的过程中发现也有人直接调用函数求最小值。

学习他人:

方法一:


小菜鸟L1

(编辑过)2021-03-08

执行用时: 0 ms , 在所有 Java 提交中击败了 100.00% 的用户 内存消耗: 37.9 MB , 在所有 Java 提交中击败了 98.34% 的用户

class Solution {
    public int minArray(int[] numbers) {
        int n=numbers.length;
        for(int i=1;i<n;i++){
            if(numbers[i]<numbers[i-1]) return numbers[i];
        }
        return numbers[0];
    }
}

方法二

码义小小

2020-10-10 还能再降低空间复杂度吗?求大佬指点

和方法一,和我本来的思路,都是一样的,但是自己的代码出错了。

//递增的性质
        /*for(int i=0;i<numbers.length-1;i++){
            if(numbers[i+1]<numbers[i])
                return numbers[i+1];
        }
        return numbers[0];*/

        //递增性质进一步思考,如果它本身就是递增的,未被旋转,第一个就是最小,
        //如果被旋转了,那么旋转后的数组第一个元素必定比最小的元素大,
        //只需要找出数组中第一个小于旋转后数组的第一个元素的数字即可
        for(int i=1;i<numbers.length;i++){
            if(numbers[i]<numbers[0])
                return numbers[i];
        }
        return numbers[0];

方法三:

夏弈L1

(编辑过)2020-07-20

要注意题目预设啊,原数组是递增的,经过旋转以后会把一部分小数据转移到后面去,所以只要从第一位往后找,只要遇到一个不是递增的元素那就说明这个元素及其之后的元素都是旋转过去的,返回这个元素就行了(因为这个小集合肯定也是递增的):

    public int minArray(int[] numbers) {
        if(numbers.length==1)
            return numbers[0];
        int i=1;
        while(i<numbers.length){
            if(numbers[i-1]>numbers[i])
                return numbers[i];
            else
                i++;
        }
        return numbers[0];
    }

方法四:


ZaklL1

2020-02-15

贴一个二分的

    public int minArray(int[] numbers) {
        int l = 0, r = numbers.length - 1;
        while (l < r) {
            int mid = ((r - l) >> 1) + l;
            //只要右边比中间大,那右边一定是有序数组
            if (numbers[r] > numbers[mid]) {
                r = mid;
            } else if (numbers[r] < numbers[mid]) {
                l = mid + 1;
             //去重
            } else r--;
        }
        return numbers[l];
    }

方法五:

官方精选二分

作者:jyd
链接:https://leetcode-cn.com/problems/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-lcof/solution/mian-shi-ti-11-xuan-zhuan-shu-zu-de-zui-xiao-shu-3/
来源:力扣(LeetCode)

class Solution {
    public int minArray(int[] numbers) {
        int i = 0, j = numbers.length - 1;
        while (i < j) {
            int m = (i + j) / 2;
            if (numbers[m] > numbers[j]) i = m + 1;
            else if (numbers[m] < numbers[j]) j = m;
            else j--;
        }
        return numbers[i];
    }
}

实际上,当出现 nums[m] = nums[j]nums[m]=nums[j] 时,一定有区间 [i, m][i,m] 内所有元素相等 或 区间 [m, j][m,j] 内所有元素相等(或两者皆满足)。对于寻找此类数组的最小值问题,可直接放弃二分查找,而使用线性查找替代。

class Solution {
    public int minArray(int[] numbers) {
        int i = 0, j = numbers.length - 1;
        while (i < j) {
            int m = (i + j) / 2;
            if (numbers[m] > numbers[j]) i = m + 1;
            else if (numbers[m] < numbers[j]) j = m;
            else {
                int x = i;
                for(int k = i + 1; k < j; k++) {
                    if(numbers[k] < numbers[x]) x = k;
                }
                return numbers[x];
            }
        }
        return numbers[i];
    }
}

总结

以上就是本题的内容和学习过程了,本题虽然简单,但是在各种边界条件,正确性的讨论上,是有加深思考的价值的。

欢迎讨论,共同进步。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值