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

题目链接:LeetCode 剑指 Offer 11. 旋转数组的最小数字

题意:

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

给你一个可能存在 重复 元素值的数组 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]] 。

解题思路:

数组中找最小值,使用 O(n) 时间复杂度的算法没什么好说的,
题干给的是一个有序的数组,所以可以使用性能更优的二分查找来做

二分查找

只有中点和右端点比较大小,才能确定哪边属于连续递增数组
存在三种情况:

  1. 中间值比最右值大,最小元素肯定出现在右半部分,且肯定不是中间值 [3,4,5,1,2]
  2. 中间值比最右值小,最小元素肯定出现在左半部分,且可能是中间值 [1,2,3,4,5]
  3. 中间值和最右值相等,可能是 [1,1,1,2,1],也可能是 [1,1,1,0,1],所以只有缩小范围再试试

代码1:

func minArray(nums []int) int {
    l, r := 0, len(nums)-1
    for l < r {
        mid := l + (r-l) >> 1
        if nums[mid] > nums[r] {
            l = mid+1
        } else if nums[mid] < nums[r] {
            r = mid
        } else {
            r--
        }
    }
    return nums[l]
}

代码2:

func minArray(numbers []int) int {
    if len(numbers) == 0 {
        return - 1
    }
    // 直接进行二分操作,划分的区间是 [l,mid] [mid+1,r]
    l := 0
    r := len(numbers) - 1
    for l < r {      
        mid := (l + r) / 2
        if numbers[mid] > numbers[r] {  //则最小值在右半部分
            l = mid + 1
        }else if numbers[mid] < numbers[r] {  //最小值在左半部分
            r = mid
        }else{
            r--   //如果相等,不确定在哪里,只能缩小范围进一步筛选
        }
    }
    // 循环退出时,l == r
    return numbers[l] 
}

YZ 的思路:使用中间值 mid 与旋转后的数据的第一个元素进行比较,(需要判断的情况比较多,没有与最右边比较好理解)

posted @ 2023-07-12 13:57  小星code  阅读(14)  评论(0)    收藏  举报