“寻找旋转排序数组中的最小值”相关题目,leetcode上有多个。
解题核心思路是:二分查找。然后,区分旋转后的不同情况做具体调整。
第一题 寻找旋转排序数组中的最小值
这道题的关键点是
- 升序排列
- 元素互不相同
- 返回最小元素
代码中,我依然考虑了有重复元素的情况。其一,考虑重复元素不会对代码执行带来额外负担,因为本题输入元素互不相同。其二,考虑重复元素,将本题和下一题的解法统一在一起了。
请通过注释了解思路。读者要多思考注释中的例子,通过反证法排除部分区间。
总结一下。对某个区间做分析,有四种可能:
- 区间左端元素小于右端元素
- 区间左端元素大于中间元素
- 区间中间元素大于右端元素
- 区间左端、中间、右端元素相等
充分利用已知条件,尽量缩小搜索区间。
解法如下(代码提交通过)。
func findMin(nums []int) int {
l := len(nums)
if l == 0 {
panic("nums is empty")
}
var (
low int
high = l-1
)
OuterLoop:
for low <= high {
// 如果当前区间左端元素小于右端元素,
// 那么该区间就是从小到大有序的,
// 如[...,1,2,3,4,5,...],1 < 5。
if nums[low] < nums[high] {
// 最小值就是左端元素。
return nums[low]
}
// 中间位置
mid := low + (high-low)>>1
v := nums[mid]
// 如果当前区间的左端元素大于中间元素,
// 如[...,6,7,1,2,3,4,5,...], 6 > 2。
// 那么最小值就在索引区间[low+1,mid]中。
// 为什么最小值不会出现在索引区间(mid,high]中呢?
// 假如最小值出现在索引区间(mid,high]中,
// 会是这样的:[...,6,7,1,2,3,-1,5],
// 它旋转之前不是有序的,
// 所以最小值不会出现在索引区间(mid,high]中。
if nums[low] > v {
// nums[low]比某个元素大,它肯定不是最小元素,所以不包含。
low++
// nums[mid]较小,可能是最小元素,要包含。
high = mid
continue
}
// 如果当前区间的中间元素大于右端元素,
// 如[...,4,5,6,7,1,2,3,...],7 > 3。
// 那么最小元素就在索引区间(mid,high]中。
// 为什么最小值不会出现在索引区间[low,mid]中,
// 根据前面的解释可同理反证出来。
if v > nums[high] {
// nums[mid]大于某个元素,它肯定不是最小值,所以不包含。
low = mid+1
// nums[high]较小,可能是最小值,所以high不变。
continue
}
// 到这里,只能是 nums[low] == nums[mid] == nums[high]。
// 为什么不写 nums[low] <= nums[mid] <= nums[high]?
// 如果存在小于的情况,就必然会在循环最开始的判断中处理,
// 即 nums[low] < nums[high] 的分支。
// 遍历该区间,尽量利用可利用的条件。
for i := low; i < high; i++ {
// 发现了一个较小的值,它就是最小值。
// 如[...,9,9,1,2,9,9,9,9,9,...]
// 或[...,9,9,9,9,9,9,1,2,9,...]
if nums[i] > nums[i+1] {
return nums[i+1]
}
// 发现一个较大的值。
// 如[...,2,3,6,1,2,2,2,2,2,...]
// 或[...,2,2,2,2,2,3,6,1,2,...]
if nums[i] < nums[i+1] {
if i+2 == mid {
// 如[...,2,2,2,5,2,2,2,2,2,...]
return v
}
// nums[i+1]肯定不是最小值,所以i+2索引作为新的low。
// 如[...,2,5,0,1,2,2,2,2,2,...],i+2 < mid。
// 如[...,2,2,2,2,2,5,0,1,2,...],i+2 > mid。
low = i+2
if i+2 < mid {
// 索引区间(mid,high]不可能出现比nums[mid]小的值了。
// 所以最小值就在索引区间[i+2,mid]中。
// nums[mid]可能是最小值,要包含它。
high = mid
}
// 注意:跳转到外层for循环
continue OuterLoop
}
}
// 全部相等,每个都是最小值
break
}
return nums[low]
}
第二题 寻找旋转排序数组中的最小值 II
这道题的关键点是
- 升序排列
- 存在重复元素
- 返回最小元素
直接使用上一道题的代码就可以解这道题,因为上一题的解法代码已经考虑重复元素了。
第三题 旋转数组的最小数字
这道题和上一题相同。