跳至主要內容

旋转数组的最小数字

soulballad算法剑指Offer剑指Offer约 623 字大约 2 分钟

题目:

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

方案一:遍历

求一个数组中的最小值,最先想到的方法一般都是遍历,然后逐个比较,循环所有元素即可取到最小值。

这种做法是时间复杂度为 O(n) 的方案。

代码:

// O(n) 时间复杂度的方案
public static int iterArray(int[] arr) {

    int min = arr[0];
    for (int num : arr) {
        if (min > num) {
            min = num;
        }
    }

    return min;
}

方案二:二分查找

先拿数组中间的元素和左右两边的元素比较:如果中间位置元素大于左边元素,说明最小元素位于右边,则查找中间-结束 这一段;如果中间位置元素小于左边,说明最小元素位于左边,则查找 开始-中间 这一段。

这种算法时间复杂度是 0(logn),效率要优于遍历。但实现复杂一些。

代码:

public static int searchArray(int[] arr) {
    int start = 0;
    int end = arr.length - 1;
    int mid = start;

    // 当最左边值大于等于最右边时候开始查找
    // 如果最左边小于最右边,说明没有反转,直接返回第一个元素
    while (arr[start] >= arr[end]) {
        // 如果此时数组只剩下两个数值
        if (end - start == 1) {
            // 最小的就是右边
            mid = end;
            break;
        }
        // 如果数组长度是2个以上
        mid = (start + end) / 2;
        // 假如最左边和中间以及最右边值都相等,只能进行顺序查找,如{1,1,1,0,1}
        if (arr[start] == arr[end] && arr[mid] == arr[start]) {
            return findMinByOrder(arr, start, end);
        }
        if (arr[mid] >= arr[start]) {  
            // 如果最左边小于等于中间,说明最小值在后半部分,把起始位置标记为mid 如{3,4,5,1,2}
            start = mid;
        } else if (arr[mid] <= arr[end]) {
            // 如果最左侧大于等于中间值,说明最小值在前半部分,把结束位置标记为mid 如{5,1,2,3,4}
            end = mid;
        }
    }

    return arr[mid];
}

private static int findMinByOrder(int[] arr, int start, int end) {
    int result = arr[start];
    for (int i = start + 1; i < end; i++) {
        if (result > arr[i]) {
            result = arr[i];
        }
    }
    return result;
}
上次编辑于:
贡献者: soulballad