算法练习第41题-搜索旋转排序数组

207 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第29天,点击查看活动详情

一、题目

整数数组 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 。

 

示例 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

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/se… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

二、思路

双指针

  • 这里的问题说是有一部分是无序的
  • 还是定义指针left = 0, right = nums.length - 1
  • while循环的判断条件是left < right
  • 五种情况
  • 第一种情况
  • nums[left] === target 返回left
  • 第二种情况
  • nums[right] === target 返回 right
  • 第三种和第四种可以合起来说,
  • 看数组,左右区间,从中间看,要么左部分有序或者右区间有序
  • 如果左区间有序且target < nums[right],那么right指针--
  • 如果右区间有序且target > nums[left],那么left++
  • 第五种情况,就是不存在的情况
  • 那么就是小于左指针,大于右指针那么就返回-1
  • 这时并没有完,本来按思路是完了的,但是要考虑长度为1的情况,
  • 单独为这种情况做下处理
  • 就是长度为1的情况下,如果值和target相等就返回0,否则返回-1

二分法

  • 首先定义数组长度,左右区间和中间值 len = nums.length, left = 0, right = len - 1

  • 首先判断特殊情况长度为1的时候,如果存在nums[0]等于target,那么返回0

  • while循环的条件是left <= right,重合的时候退出循环

  • 三种情况

  • 第一种

  • 如果midNum 等于 target,那么返回mid

  • 第二种和第三种情况

  • 判断[0, mid]和[mid, right]哪边有序

  • 如果[0, mid]有序,那么判断在当前有序的区间中

  • 如果nums[0] <= target 且 target < midNum,那么可以缩小right

  • 否则则增大left

  • 不满足[0, mid]有序的情况

  • 如果 midNum < target 且 target <= nums[len - 1]

  • 那么左区间left 增加

  • 否则 right 减小

  • 如果都不满足条件

  • 那么最后返回return -1

三、代码

双指针代码

let nums = [4,5,6,7,0,1,2], target = 0
// let nums = [1], target = 0
let search = function(nums, target) {
  /**
   * 思路双指针
   * 这里的问题说是有一部分是无序的
   * 还是定义指针left = 0, right = nums.length - 1
   * while循环的判断条件是left < right
   * 五种情况
   * 第一种情况
   * nums[left] === target 返回left
   * 第二种情况
   * nums[right] === target 返回 right
   * 第三种和第四种可以合起来说,
   * 看数组,左右区间,从中间看,要么左部分有序或者右区间有序
   * 如果左区间有序且target < nums[right],那么right指针--
   * 如果右区间有序且target > nums[left],那么left++
   * 第五种情况,就是不存在的情况
   * 那么就是小于左指针,大于右指针那么就返回-1
   * 
   * 
   * 这时并没有完,本来按思路是完了的,但是要考虑长度为1的情况,
   * 单独为这种情况做下处理
   * 就是长度为1的情况下,如果值和target相等就返回0,否则返回-1
   * */ 
  let len = nums.length, left = 0 , right = len - 1
  if(len == 1 && nums[0] == target){
    return 0
  }
  while(left < right){
    let leftNum = nums[left], rightNum = nums[right]
    if(target < leftNum && target > rightNum){
      return -1
    }
    if(target == leftNum){
      return left
    }
    if(target == rightNum){
      return right
    }
    if(left < right && target < rightNum){
      right--
    }else if(left < right && target > leftNum){
      left++
    }
  }
  return -1
}

二分查找

let nums = [4,5,6,7,0,1,2], target = 7
// let nums = [1], target = 0
// let nums = [3,1], target = 1
let search = function(nums, target) {
  /**
   * 二分法
   * 首先定义数组长度,左右区间和中间值 len = nums.length, left = 0, right = len - 1
   * 首先判断特殊情况长度为1的时候,如果存在nums[0]等于target,那么返回0
   * while循环的条件是left <= right,重合的时候退出循环
   * 三种情况 
   * 第一种
   * 如果midNum 等于 target,那么返回mid
   * 
   * 第二种和第三种情况
   * 判断[0, mid]和[mid, right]哪边有序
   * 如果[0, mid]有序,那么判断在当前有序的区间中
   *    如果nums[0] <= target 且 target < midNum,那么可以缩小right
   *    否则则增大left
   * 不满足[0, mid]有序的情况
   *    如果 midNum < target 且 target <= nums[len - 1]
   *    那么左区间left 增加
   *    否则 right 减小
   * 如果都不满足条件
   * 那么最后返回return -1
   * */ 
  let len = nums.length, left = 0 , right = len - 1
  if(len == 1 && nums[0] == target){
    return 0
  }
  while(left <= right) {
    let mid = left + Math.floor((right - left)>>1)
    let midNum = nums[mid]
    if(midNum === target) {
      return mid
    }
    if(nums[0]<= midNum) {
      if(nums[0] <= target && target < midNum) {
        right = mid - 1
      } else {
        left = mid + 1
      }
    } else {
      if(midNum < target && target <= nums[len - 1]) {
        left = mid + 1
      } else {
        right = mid - 1
      }
    }
    
  }
  return -1
}

四、测试结果

双指针测试结果

image.png

二分查找测试结果

image.png