剑指offer—面试题11:旋转数组的最小数字

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

最简单的方法就是遍历,直接遍历一次数组找到其中的最小值,时间复杂度O(n)。

算法一:

   func minArray(_ numbers: [Int]) -> Int {
        guard numbers.count > 0 else {
            return  -1
        }
        var min = numbers[0]
        for num in 1.. numbers[num] {
                min =  numbers[num]
            }
        }
        return min
    }

算法一是一种暴力的解法,我们可以根据旋转数组的特性来优化,旋转后的数组实际上可以划分为两个排序好的子数组。而且前面的子数组元素都大于后面子数组的元素。而最小元素是这两个子数组的分界线。在排序的数组查找元素我们可以通过二分查找提供性能。该题给出的数组一定程度上也是排序的,所以也可以通过二分查找来寻找最小元素。

在二分查找的每一步中,左边界为low,右边界为high,区间的中点为 indexMid,最小值就在该区间内。我们将中轴元素 numbers[indexMid]与右边界元素 numbers[high] 进行比较,可能会有以下的三种情况:

第一种情况是 numbers[indexMid]。这说明numbers[indexMid]是最小值右侧的元素,因此我们可以忽略二分查找区间的右半部分。

第二种情况是 numbers[indexMid]>numbers[high]。这说明 numbers[indexMid]是最小值左侧的元素,因此我们可以忽略二分查找区间的左半部分。

第三种情况是 numbers[indexMid]==numbers[high]。如下图所示,由于重复元素的存在,我们并不能确定numbers[indexMid]究竟在最小值的左侧还是右侧,因此我们不能莽撞地忽略某一部分的元素。我们唯一可以知道的是,由于它们的值相同,所以无论numbers[high]是不是最小值,都有一个它的「替代品」numbers[indexMid],因此我们可以忽略二分查找区间的右端点。

算法二:

 func minArray(_ numbers: [Int]) -> Int {
    var low = 0
    var high = numbers.count - 1
    
    while low < high {
        let indexMid = low + (high - low)/2
        
        if numbers[indexMid] < numbers[high] {
            high = indexMid
        }else if numbers[indexMid] > numbers[high] {
            low = indexMid + 1
        }else {
            high -= 1
        }
    }
    
    return numbers[low]
}

时间复杂度:平均时间复杂度为 O(\log n)O(logn)

你可能感兴趣的:(剑指offer—面试题11:旋转数组的最小数字)