解法一:二分查找变种
二分搜索算法不仅能应用在排好序的数组,如果这个数组不是一个标准的有序数组,只要稍微修改算法逻辑,也能使用二分搜索。还是那句话,二分思想的核心在于快速收缩搜索区间。
思路分析:把一个排好序的数组就好比一段斜向上的山坡,沿着一个元素旋转数组,相当于将山坡切断并旋转,在原本平滑的山坡上产生一个「断崖」,「断崖」左侧的所有元素比右侧所有元素都大
可以在这样一个存在断崖的山坡上用二分搜索算法搜索元素的,主要分成两步:
- 确定中点mid是落在断崖左侧还是右侧;
- 根据 target 和 nums[left], nums[right], nums[mid] 的相对大小收缩搜索区间
假设nums[mid]刚好等于target,那么直接找到答案。
假设 mid 在「断崖」左侧,那么可以肯定 nums[left..mid] 是连续且有序的,可以在这段范围内执行二分查找,所以如果 nums[left] <= target < nums[mid],则可以收缩右边界(right = mid-1),否则应该收缩左边界(left = mid+1)。
假设 mid 在「断崖」右侧,那么可以肯定 nums[mid..right] 是连续且有序的,可以在这段范围内执行二分查找,所以如果 nums[mid] < target <= nums[right],则可以收缩左边界(left = mid+1),否则应该收缩右边界(right = mid-1)。
func search(nums []int, target int) int {
left, right := 0, len(nums)-1
for left <= right{
mid := left + (right - left)/2
if nums[mid] == target {
return mid
}
if nums[mid] >= nums[left]{ // nuns[mid]处于左半边,此时 nums[left..mid] 有序
if target >= nums[left] && target < nums[mid]{
right = mid - 1
}else{
left = mid + 1
}
}else { // nuns[mid]处于右半边,此时 nums[mid..right] 有序
if target <= nums[right] && target > nums[mid]{
left = mid+1
}else{
right = mid-1
}
}
}
return -1
}