前端刷题路-Day35:搜索旋转排序数组 II(题号81)

搜索旋转排序数组 II(题号81)

题目

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

提示:

  • 1 <= nums.length <= 5000
  • -104 <= nums[i] <= 104
  • 题目数据保证 nums 在预先未知的某个下标上进行了旋转
  • -104 <= target <= 104

进阶:

这是 搜索旋转排序数组 的延伸题目,本题中的 nums 可能包含重复元素。
这会影响到程序的时间复杂度吗?会有怎样的影响,为什么?

链接

leetcode-cn.com/problems/se…

解释

这波啊,这波是愣头青。

一开始的题目看得我十分迷惑,这不就一个includes么?直接完事了。提交的时候颤颤巍巍,生怕自己理解错了。但实际上并没有,的的确确一行代码就完事了。

但真的有这么简单么?提交完成后确实没问题,但性能消耗在后30%左右,这并不是一个好消息。

它代表的意义很简单,做是做出来的,但是最愚蠢的办法。

笔者又思考了一会,发现这题的数组本来是升序的(包含重复元素),但是处理之后就被截断了,但是数组的前半部分和后半部分依然是升序的。

所以这里只需要找到这个那个断点,利用全都是升序的特点,判断一下第一个元素的值是否大于target,如果大于,那么只要去第二部分找就行,如果不大于,只要去第一部分找就行。

查找的方法依然是用的includes,但性能直接到了70%,甚至有时候速度可以到90%以上,强行提升了一手。

问题就在于判断断点在哪里,笔者知道这里考的是二分,可情况太多了,实在想不出来了,木得办法。

自己的方法

var search = function(nums, target) {
  // 初始化变量
  var mid = 0
      len = nums.length
      start = 0
      end = len - 1
  // 找到断点
  for (let i = 0; i < len; i++) {
    if (nums[i + 1] < nums[i]) {
      mid = i
      break
    }
  }
  // 判断target在前一部分还是后一部分
  if (target >= nums[0]) {
    start = 0
    end = mid + 1
  } else {
    start = mid
    end = len
  }
  // 如果断点在数组的第一位,直接从数组中找就行,否则取数组中的一部分进行查找
  nums = !mid ? nums : nums.slice(start, end)
  return nums.indexOf(target) > -1
};
复制代码

这段代码的注释十分丰富,笔者就不多赘述了。

更好的方法(二分)

二分这里其实空讲有些难,推荐看看另外一题的答案这里,这里更简单一些,数组中没有重复元素,如果弄懂了简单题的二分,复杂题的二分其实就多一步处理重估数据的操作。

那么什么时候进行数据去重呢?在左元素、中间元素、右元素三者全部相同时,此时已经无法判断子区间的升序降序,则可以将左元素向右移动一位,将右元素向左移动一位,这样得到一个新的区间再进行迭代。

代码整体风格那简单题一样,就多了一个去重打条件?:

var search = function(nums, target) {
  var len = nums.length
  if (len === 1) return nums[0] === target
  var left = 0
      right = len - 1
  while (left <= right) {
    var mid = ~~((left + right) / 2)
    if (nums[mid] === target) return true
    // 就是这一行
    if (nums[left] === nums[mid] && nums[mid] === nums[right]) {
      left++
      right--
    } else if (nums[left] <= nums[mid]) {
      if (nums[left] <= target && target < nums[mid]) {
        right = mid - 1
      } else {
        left = mid + 1
      }
    } else {
      if (nums[mid] < target && target <= nums[right]) {
        left = mid + 1
      } else {
        right = mid - 1
      }
    }
  }
  return false
};
复制代码

别的也就没啥了,和没有重复数据那题一样。

PS:想查看往期文章和题目可以点击下面的链接:

这里是按照日期分类的?

前端刷题路-目录(日期分类)

经过有些朋友的提醒,感觉也应该按照题型分类
这里是按照题型分类的?

前端刷题路-目录(题型分类)

有兴趣的也可以看看我的个人主页?

Here is RZ

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享