剑指Offer——旋转数组最小数字

本文围绕在旋转数组中找最小数字的问题展开,给出三种解题思路。思路一是对数组升序排序后取首项,时间复杂度为O(NlogN);思路二结合旋转数组性质遍历数组,时间复杂度为O(N);思路三采用二分法,进一步降低时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接

https://www.nowcoder.com/practice/9f3231a991af4f55b95579b44b7a01ba?tpId=13&tqId=11159&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

 

题目

 

函数接口

 

题解

抛开该数组为一个旋转数组这点。本题的实质就是在一个数组中找到最小数字。

一个很简单的方式就是对数组进行排序,使得数组为一个升序数组,然后输出数组第一项即可。

 

思路一

对整个数组进行升序排序,然后返回数组第一项。因为整个数组第一项(即整个数组的最小项)必然也是旋转数组的最小项。

实现代码如下

public int minNumberInRotateArray(int[] array) {
    if (array.length == 0 || array == null)
        return 0;
    Arrays.sort(array);
    return array[0];
}

思路一这种方法虽然简单易行,代码量少。但因为利用了快排,所以时间复杂度为O(NlogN),因为计算过程中的许多排序操作,对于解题本身的意义并不大,所以产生了很多不必要的开销。

回想一下在学校时,在一个数组中查找最大/最小值的方法。不难想起一简单种易行,且时间复杂度为O(N)的方法。

 

思路二

在学校学习时,在一个有序/无序数组中查找最大/最小值的方法。最简单的一种,就是遍历整个数组,这样的时间复杂度也不过是O(N)。

但是对于该题,结合旋转数组的性质,笔者对于这种思路进行了一点小的调整。

首先对问题进行归约,对于一个朴素的旋转数组(即不存在重复的数组,且已经存在若干个元素的旋转),那么从后往前遍历。那么数值必然是先递减,再在某个位置断崖式的上升,然后再递减。

例如:4 5 6 7 8 9 10 1 2 3

3到1时是递减,然后在10这个位置上升,10到4继续保持递减。

不难发现,如果是这种情况,那么那个上升点的位置(本例中即是1),即是旋转数组最小的数字。

那么我们再对问题进行推广

对于一个非朴素的旋转数组(包含重复数字,或没有元素旋转)

给出以下三个样例:

1 2 2 2 3 3 3 1 1

1 1 1 1 1 1 1 1 1

1 2 3 4 5 6 7 8 9

不难发现,最小数字所处位置都是在数组第一项。

将以上的推论进行整理,即可得出思路二的一个实现

public int minNumberInRotateArray(int[] array) {
    if (array.length == 0 || array == null)
        return 0;
    for (int i = 1; i < array.length; i++) {
        if (array[i] < array[i - 1]) {
            return array[i];
        }
    }
    return array[0];
}

 

思路三

思路二相比思路一,在时间复杂度和辅助空间复杂度上都有所提升(块排采用递归实现,栈的使用和断点信息的保存都会带来空间和时间的开销)。

相比而言,思路二已经是较为可行的一种思路。

那么还有没有一种方法可以继续降低时间复杂度呢?

先回想一下,如果时间开销要比O(N)更低,那么常见的就有O(根号N),O(logN),O(1)。

对于笔者,看到O(logN)这个复杂度,第一反应就是二分法,那么对于本题。是否可以采用二分法呢?

答案是可以的

同样先对问题进行归约,对于一个朴素的旋转数组

例如:4 5 6 7 8 9 1 2 3

首先取得其中项8,若中项大于末项。那么就对后半段进行下一步操作。

得:9 1 2 3

其中项是1,由于中项小于末项,那么就对前半段进行操作。

得:9 1

同理,其中项是9,中项大于末项,继续对后半段进行操作。

得:1

由于只剩一个元素,所以该元素就是问题答案。

那么我们对于问题进行推广,同样采用以下三组样例:

1 2 2 2 3 3 3 1 1

1 1 1 1 1 1 1 1 1

1 2 3 4 5 6 7 8 9

简单进行一下模拟,发现同样能得到正解。

对于以上思路进行归纳整理,得到该思路的一个实现

public int minNumberInRotateArray(int[] array) {
    if (array.length == 0 || array == null)
        return 0;
    int index = binarySearchMinNumber(0, array.length - 1, array);
    return array[index];
}

public int binarySearchMinNumber(int left, int right, int[] array) {
    int mid = (left + right) / 2;

    if (left == right) {
        return left;
    } else if (array[mid] > array[right]) {
        return binarySearchMinNumber(mid + 1, right, array);
    } else {
        return binarySearchMinNumber(left, mid, array);
    }
}

以上三种思路是笔者对于本题的一些简单思考。如果有更优异的解法,欢迎大家分享。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值