搜索旋转排序数组 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
可能包含重复元素。
这会影响到程序的时间复杂度吗?会有怎样的影响,为什么?
链接
解释
这波啊,这波是愣头青。
一开始的题目看得我十分迷惑,这不就一个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:想查看往期文章和题目可以点击下面的链接:
这里是按照日期分类的?
经过有些朋友的提醒,感觉也应该按照题型分类
这里是按照题型分类的?
有兴趣的也可以看看我的个人主页?