33. 搜索旋转排序数组
整数数组 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) 的算法解决此问题。
解: 二分。在二分的过程中,设置两个变量分别为 flag :判断target是否大于最后一个数。 isIncremental: 从中点到末尾是否递增。
二分的三个分支:中点值小于target;中点值等于target;中点值大于target。 其中等于的时候直接返回即可。
其余两个分支的可能性分析
分支一:中点值小于target;
- flag为true时,即target比最后一个值大,中点又比target小。如果此时mid到right是递增的,说明mid右边不可能找到target,所以边界左移right = mid - 1。如果不是递增,说明mid左边不可能找到target,边界右移left = mid - 1
- flag为false时,即target比最后一个值小,中点又比target小。如果此时mid到right是递增的,说明target就在mid的右边,所以边界右移left = mid + 1。画个图就可以发现不存在不递增的情况,所以不递增不用考虑
分支二:中点值大于target,跟分支一分析方法同理
const search = function(nums, target) {
let left = 0;
let right = nums.length - 1
while (left <= right) {
const mid = ~~((left + right) / 2)
const flag = target > nums[right]
const isIncremental = nums[mid] < nums[right]
if (nums[mid] === target) return mid
if (nums[mid] > target) {
if (!isIncremental && !flag) {
left = mid + 1
}
if (isIncremental && !flag || !isIncremental && flag) {
right = mid - 1
}
} else {
if (isIncremental && flag) {
right = mid - 1
}
if (isIncremental && !flag || !isIncremental && flag) {
left = mid + 1
}
}
}
return -1
};