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

48 阅读2分钟

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。

给你一个可能存在 重复 元素值的数组 numbers ,它原来是一个升序排列的数组,并按上述情形进行了一次旋转。请返回旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一次旋转,该数组的最小值为 1。

注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。

示例 1:

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

解题思路

有序数组中的查找,优先考虑二分法查找,二分法的关键在于如何通过比较缩小查找范围。

数组[3,4,5,1,2][3,4,5,1,2] 可以分为左右两个数组——左数组[3,4,5][3,4,5]和右数组[1,2][1,2],我们需要查找的targettarget就是右数组的第一个数。

所以本题的解题思路的就是通过判断midmid处于哪个左数组还是右数组来缩小查找的范围,最后把范围缩小到右数组的第一个数,即可查找到数组中的最小数字。

那么,如何判断midmid处于左数组还是右数组呢?

——只需要与rightright^[1]^进行比较即可。

right\because right是右数组的最大值

\therefore 右数组的所有数都<right< right,左数组的所有数都>right> right

\therefore 如果mid<rightmid < right说明midmid处于右数组,如果mid>rightmid > right说明midmid处于左数组

现在我们将midmidrightright进行比较,如果mid>rightmid > right,就移动leftleftmid+1mid+1处,如果mid<rightmid<right,就移动rightrightmidmid^[2]^处,通过这种方式即可完成最小值的二分查找。

算法流程

  1. 初始化:声明leftrightleft、right两个指针分别指向数组的两端
  2. 循环二分mid=(left+right)/2mid=(left+right)/2rightright进行比较有以下几种情况
    • mid>rightmid>right,说明midmid位于左数组,右移leftleftmid+1mid+1
    • mid<rigthmid<rigth,说明midmid位于右数组,左移rightrightmidmid
    • mid=rigthmid=rigth^[3]^,无法判断,rightright--
  3. 返回值left=rigthleft=rigth时,结束循环,此时leftleft指向的就是数组的最小值

正确性证明:

  1. 能否与leftleft 进行比较?

    不能,虽然对于数组[3,4,5,1,2][3,4,5,1,2] 看起来是可行的,但如果没有数字被旋转,即[1,2,3,4,5][1,2,3,4,5] 这类纯有序数组,则所有midmid 都大于leftleft ,无法确定其处于哪个数组。

  2. 为什么leftleft 是移动到mid+1mid+1 处,而rightright 是移动到midmid 处?

    因为mid<rightmid<right 时,midmid 可能就是我们要求的最小值。

  3. mid=rightmid=right 时,为什么可以通过rightright-- 缩小范围?

    因为mid=rightmid=right 时,rigthrigth 肯定不是右排序数组的第一个元素。

代码

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