题目链接

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

 

题目

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

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

 

函数接口

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

 

题解

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

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

 

思路一

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

实现代码如下

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);
    }
}

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

相关文章:

  • 2021-05-30
  • 2021-06-16
  • 2022-12-23
  • 2021-12-09
猜你喜欢
  • 2021-06-06
  • 2021-12-09
  • 2022-01-09
  • 2021-10-22
  • 2021-09-30
  • 2021-08-01
  • 2021-09-29
相关资源
相似解决方案