算法学习记录(八十三)

61 阅读1分钟

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) 的算法解决此问题。

image.png

解: 二分。在二分的过程中,设置两个变量分别为 flag :判断target是否大于最后一个数。 isIncremental: 从中点到末尾是否递增。

二分的三个分支:中点值小于target;中点值等于target;中点值大于target。 其中等于的时候直接返回即可。

其余两个分支的可能性分析

分支一:中点值小于target;

  1. flag为true时,即target比最后一个值大,中点又比target小。如果此时mid到right是递增的,说明mid右边不可能找到target,所以边界左移right = mid - 1。如果不是递增,说明mid左边不可能找到target,边界右移left = mid - 1
  2. 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
};