整数数组 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,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
示例 1:
输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4
示例 2:
输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1
示例 3:
输入: nums = [1], target = 0
输出: -1
提示:
1 <= nums.length <= 5000-104 <= nums[i] <= 104nums中的每个值都 独一无二- 题目数据保证
nums在预先未知的某个下标上进行了旋转 -104 <= target <= 104
因为题目要求算法的时间复杂度为O(log n)。二分查找是一种高效的搜索算法,可以在有序数组中快速定位目标值的位置。
在给定的旋转排序数组中,虽然数组经过旋转,但仍然可以分为两个有序的子数组。例如,[4,5,6,7,0,1,2] 经过旋转后可以分为 [4,5,6,7] 和 [0,1,2] 两个有序子数组。
使用二分查找的思想,我们可以通过比较目标值与中间元素的大小来确定目标值可能在哪个有序子数组中。然后,根据有序子数组的特性,我们可以缩小搜索范围,并继续进行二分查找。
具体的分析思路如下:
-
初始化左指针
left为数组的起始位置,右指针right为数组的结束位置。 -
在循环中,通过计算中间位置
mid,将数组分为两部分。 -
比较中间元素
nums[mid]与目标值target的大小:-
如果
nums[mid]等于target,则找到目标值,返回mid。 -
如果
nums[mid]大于等于nums[left],说明左半部分是有序的:- 如果
target大于等于nums[left]且小于nums[mid],则目标值可能在左半部分,更新right为mid-1。 - 否则,目标值可能在右半部分,更新
left为mid+1。
- 如果
-
如果
nums[mid]小于nums[left],说明右半部分是有序的:- 如果
target大于nums[mid]且小于等于nums[right],则目标值可能在右半部分,更新left为mid+1。 - 否则,目标值可能在左半部分,更新
right为mid-1。
- 如果
-
-
重复步骤2和步骤3,直到找到目标值或左指针大于右指针。
-
如果循环结束时仍未找到目标值,返回-1表示未找到。
通过以上的分析思路,我们可以使用二分查找的思想在O(log n)的时间复杂度内找到旋转排序数组中的目标值。
代码:
package main
import "fmt"
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[left] <= nums[mid] {
if target >= nums[left] && target < nums[mid] {
right = mid - 1
} else {
left = mid + 1
}
} else {
if target > nums[mid] && target <= nums[right] {
left = mid + 1
} else {
right = mid - 1
}
}
}
return -1
}
func main() {
nums := []int{4, 5, 6, 7, 0, 1, 2}
target := 0
result := search(nums, target)
fmt.Println(result)
}
在上述代码中,search函数使用二分查找的思想来搜索旋转排序数组中的目标值。算法的时间复杂度为O(log n)。
在主函数中,我们定义了一个旋转排序数组nums和目标值target,然后调用search函数来查找目标值在数组中的下标。最后,打印出结果。
在算法实现中,首先定义左右两个指针left和right分别指向数组的开始和结束位置。然后,在循环中,通过计算中间位置mid,将数组分为两部分进行查找。根据旋转数组的特性,判断目标值可能在哪一部分,并更新左右指针的位置。最终,当找到目标值时,返回其下标;否则,返回-1表示未找到。