【LeetCode】153. 寻找旋转排序数组中的最小值

leetcode

 

解题思路

本题要求在旋转排序数组中寻找最小元素,数组元素互不相同且满足旋转数组的特性(由两个有序子数组组成,前子数组所有元素大于后子数组所有元素)。核心思路是利用二分查找,通过比较中间元素与右边界元素的值,逐步缩小搜索范围,最终定位到最小元素。

关键特性

  • ​​旋转数组结构​​:旋转后的数组由两个有序子数组组成,前子数组所有元素大于后子数组所有元素。
  • ​​最小元素位置​​:最小元素位于后子数组的开头,即旋转点。
  • ​​时间复杂度要求​​:必须设计 O(log n) 的算法,因此需用二分查找。

关键步骤

  1. ​​初始化指针​​:设置左指针 left = 0,右指针 right = len(nums)-1
  2. ​​二分循环​​:
    • ​​计算中点​​:mid = left + (right - left) / 2(防溢出)。
    • ​​比较中点与右边界​​:
      • ​​若 nums[mid] > nums[right]​​:说明中点在前子数组,最小元素在右侧(mid+1 到 right),更新 left = mid + 1
      • ​​若 nums[mid] < nums[right]​​:说明中点在后子数组,最小元素在左侧(left 到 mid),更新 right = mid
  3. ​​终止条件​​:当 left == right 时,找到最小元素 nums[left]

​​为何比较 nums[mid] 和 nums[right]?​​
因为旋转数组中,前子数组元素均大于后子数组元素。通过比较中点与右边界,可明确判断中点所在子数组,从而缩小搜索范围。


代码实现

func findMin(nums []int) int {
    left, right := 0, len(nums)-1
    for left < right {
        mid := left + (right-left)/2 // 防溢出计算中点
        if nums[mid] > nums[right] {
            left = mid + 1 // 最小元素在右侧
        } else {
            right = mid // 最小元素在左侧(含中点)
        }
    }

    return nums[left]
}

代码解析

  1. ​​循环条件​​:left < right 确保在指针相遇时终止循环。
  2. ​​中点计算​​:left + (right-left)/2 避免大数溢出。
  3. ​​分支逻辑​​:
    • nums[mid] > nums[right] → 中点在前子数组 → 最小元素在 mid 右侧。
    • nums[mid] < nums[right] → 中点在后子数组 → 最小元素在 mid 左侧(含 mid)。
  4. ​​返回值​​:最终 left 指向最小元素。

示例测试

func main() {
    // 示例1:最小元素在旋转点
    nums1 := []int{3, 4, 5, 1, 2}
    fmt.Println(findMin(nums1)) // 输出: 1

    // 示例2:最小元素在数组后半段
    nums2 := []int{4, 5, 6, 7, 0, 1, 2}
    fmt.Println(findMin(nums2)) // 输出: 0

    // 示例3:无旋转(完全升序)
    nums3 := []int{11, 13, 15, 17}
    fmt.Println(findMin(nums3)) // 输出: 11

    // 边界测试:单元素数组
    nums4 := []int{5}
    fmt.Println(findMin(nums4)) // 输出: 5

    // 特殊测试:最小元素为首元素(旋转后与原数组相同)
    nums5 := []int{2, 3, 4, 5, 1}
    fmt.Println(findMin(nums5)) // 输出: 1
}
 

复杂度分析

​​指标​​​​值​​​​说明​​
​​时间复杂度​​ O(log n) 每次循环将搜索范围减半,最坏情况需 log n 次比较
​​空间复杂度​​ O(1) 仅使用常数级别的额外空间

关键点总结

  1. ​​二分查找的适用性​​:
    利用旋转数组的局部有序性,通过比较中点与右边界元素,将时间复杂度优化至 O(log n)。

  2. ​​指针更新逻辑​​:

    • ​​left = mid + 1​​:当 nums[mid] > nums[right] 时,mid 一定不是最小元素。
    • ​​right = mid​​:当 nums[mid] < nums[right] 时,mid 可能是最小元素,故保留。
  3. ​​终止条件​​:
    循环终止时 left == right,指向最小元素,无需额外判断。

  4. ​​边界处理​​:

    • 单元素数组直接返回。
    • 完全升序数组(无旋转)通过 nums[mid] < nums[right] 分支正确处理。

​​为何不用比较左边界?​​
比较左边界(如 nums[mid] 与 nums[left])无法明确区分中点所在子数组(例如旋转点左侧可能连续递增),而比较右边界可避免此问题。

posted @ 2025-06-19 11:44  云隙之间  阅读(41)  评论(0)    收藏  举报