这是索引二分的第四篇算法,力扣链接
已知存在一个按非降序排列的整数数组
nums
,数组中的值不必互不相同。在传递给函数之前,
nums
在预先未知的某个下标k
(0 <= k < nums.length
)上进行了 旋转 ,使数组变为[nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]]
(下标 从 0 开始 计数)。例如,[0,1,2,4,4,4,5,6,6,7]
在下标5
处经旋转后可能变为[4,5,6,6,7,0,1,2,4,4]
。给你 旋转后 的数组
nums
和一个整数target
,请你编写一个函数来判断给定的目标值是否存在于数组中。如果nums
中存在这个目标值target
,则返回true
,否则返回false
。你必须尽可能减少整个操作步骤。
示例 1:
输入:nums = [2,5,6,0,0,1,2], target = 0 输出:true示例 2:
输入:nums = [2,5,6,0,0,1,2], target = 3 输出:false
老规矩,先上无脑暴力求解,这道题就是普通的旋转数组,呈断崖式递增,和之前的旋转数组类似,最本质的区别是当前的题会有重复的场景。
func search(nums []int, target int) bool {for _, num := range nums {if num == target {return true}}return false
}
二分法逻辑其实和旋转数组也差不多,借此机会复习一下。
这里要分情况讨论,先确定target位于的区间。
如果target在左区间(target >= nums[0]):
- 如果mid位于左区间(nums[mid] > nums[0]),正常二分法可以找到解。
- 如果mid位于右区间(nums[mid] < nums[0]),尽量使右指针左移到左边界。
如果target在右边界(target < nums(len(nums)-1)
- 如果mid位于左区间(nums[mid] > nums[0]),尽量使左指针靠近右区间左边界。
- 如果mid位于右区间(nums[mid] < nums[0]),正常二分法可以求解。
得到代码如下:
func search(nums []int, target int) bool {l, r := 0, len(nums)-1for l <= r {mid := l + (r-l)/2if nums[mid] == target {return true}if target >= nums[0] {if nums[mid] >= nums[0] {if nums[mid] < target {l = mid + 1} else {r = mid - 1}} else {r = mid - 1}} else {if nums[mid] <= nums[len(nums)-1] {if nums[mid] < target {l = mid + 1} else {r = mid - 1}} else {l = mid + 1}}}return false
}
执行发现,并没通过,在[1,0,1,1,1]找0的场景报错了,为什么呢?因为nums[l] == nums[mid] == nums[r] 并不能判断出来,当前位于哪个区间。
我们可以尝试 l++ r-- 缩小一下范围,这样就没有左右区间模糊的场景了。
但是还有一个问题,原来的整体坐标边界参考是nums[0]和nums[len(nums)-1],在缩小范围后,也会随之变化为nums[l] 和 nums[r]。
得到代码如下:
func search(nums []int, target int) bool {l, r := 0, len(nums)-1for l <= r {mid := l + (r-l)/2if nums[mid] == target {return true}if nums[l] == nums[r] && nums[l] == nums[mid] {l++r--continue}if target >= nums[l] {if nums[mid] >= nums[l] {if nums[mid] < target {l = mid + 1} else {r = mid - 1}} else {r = mid - 1}} else {if nums[mid] <= nums[r] {if nums[mid] < target {l = mid + 1} else {r = mid - 1}} else {l = mid + 1}}}return false
}
逻辑优化:
func search(nums []int, target int) bool {l, r := 0, len(nums)-1for l <= r {mid := l + (r-l)/2if nums[mid] == target {return true}if nums[l] == nums[r] && nums[l] == nums[mid] {l++r--continue}if target >= nums[l] {if nums[mid] >= nums[l] && nums[mid] < target {l = mid + 1} else {r = mid - 1}} else {if nums[mid] <= nums[r] && nums[mid] > target {r = mid - 1} else {l = mid + 1}}}return false
}