[路飞]_搜索旋转排序数组 II

155 阅读1分钟

「这是我参与2022首次更文挑战的第12天,活动详情查看:2022首次更文挑战

81. 搜索旋转排序数组 II

题目

已知存在一个按非降序排列的整数数组 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

题解

枚举

数组中是否存在目标值target,一次循环查找数组中所有元素找到target返回true否则返回false即可

但是,这样时间复杂度为O(n);并且没有用到数组是有规律的这个特性。并且题目中明确要求必须尽可能减少整个操作步骤。

有规律的数组寻找目标值,常见的的方式是二分

二分法

因为数组在有序的基础上进行了旋转,所以原数组应该是1个或者2个有序数组组成的;

  • 当旋转下标是0时,数组有序;
  • 当旋转下标为k时,数组在[0,lenk],[lenk+1,len][0,len-k],[len-k+1,len]区间有序

知道这个规律,首先通过枚举找到k,在通过二分法找到 target;

这里用到了二分法,但是并不是最优解,只是更容易理解的二分法

var search = function(nums, target) {
    const len = nums.length;

    let f = 0
    for(let i = 1 ;  i< len ; i++){
        if(nums[i] >= nums[i-1]){
            f = i
        }else{
            break
        }

    }

    return helper(0,f) || helper(f+1,len)

    function helper (left,right){
        while(left <= right){
            const m = left +( (right - left) >>1)
            if(nums[m] === target) return true;
            if(nums[m] > target){
                right = m-1
            }else{
                left = m+1
            }
        }
        return false

    }

};

最优解二分法

数组虽然经过旋转,但是数组的规律还在,在二分的时候,一定最少有一个数组是有序的;
常规二分法在[0,n]区间;

  • left=0left = 0
  • right=n1right = n-1;
  • mid=(left+right)/2mid = (left + right)/2
  • mid>targetmid > target,在[0,mid1][0,mid-1]区间查找
  • mid<targetmid < target,在[mid+1,n][mid+1,n]区间查找
  • mid=targetmid = target,返回 midmid

旋转数组,二分过程中可能会产生一个无序的数组,所以要找到这个无序数组

  • left=0left = 0
  • right=n1right = n-1;
  • mid=(left+right)/2mid = (left + right)/2
  • 从这里就开始变化了
  • left<midleft < mid,如果中间值大于左侧值,数组在[left,mid][left,mid]区间有序,
    • left<=target<=midleft <= target <= mid , targettarget 一定在[left,mid][left,mid]区间
    • 否则在[mid+1,right][mid+1,right]区间
  • left>midleft > mid,数组左侧大于右侧,这是个无序数组,无序数组的另一侧时有序数组
    • 通过mid<=target<=rightmid <= target <= right 添加成立判断 targettarget 是否在[mid+1,right]
    • 否则在[left,mid]
  • left==midleft == mid ,特殊处理,因为数组中的值不必互不相同,遇到[1,0,1,1,1];
    • 此时应该如果目标值 left===midleft ===mid 返回
    • 否则尝试在 [left+1,right][left+1,right]区间查找 targettarget

根据上述思路编辑代码如下

var search = function (nums, target) {
  let left = 0
  let right = nums.length - 1
  while (left < right) {
    let mid = left + Math.floor(right - left) / 2
    if (nums[left] < nums[mid]) {
      if (nums[left] <= target && target <= nums[mid]) {
        right = mid
      } else {
        left = mid + 1
      }
    } else if (nums[left] > nums[mid]) {
      if (nums[mid] < target && target <= nums[right]) {
        left = mid + 1
      } else {
        right = mid
      }
    } else {
      if (nums[left] == target) return true
      left = left + 1
    }
  }
  return nums[left] == target
}