搜索旋转排序数组(题号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
。
示例 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
复制代码
提示:
1 <= nums.length <= 5000
-10^4 <= nums[i] <= 10^4
nums
中的每个值都 独一无二- 题目数据保证
nums
在预先未知的某个下标上进行了旋转 -10^4 <= target <= 10^4
进阶:你可以设计一个时间复杂度为 O(log n) 的解决方案吗?
链接
解释
还记得Day35那篇文章么(传送们:Day35:搜索旋转排序数组 II(题号81)。这题是那一题的简单版本,里面不带重复数字的。
不带重复数字就简单一些了,下面好好分析一下。
首先,数组是升序的,由小到大,在某一个点旋转后,数组就会变成两个部分,一部分是从小到大,一部分从大到小。还有一种可能性是数组不旋转,一直都是从小到大。
那么这题就可以用双指针来做了,可以说是普通双指针的变种吧。
首先定位左右指针,一个在前一个在后,开启循环,条件是是左指针小于等于右指针。
下面是循环步骤:
- 拿到
mid
,取左右指针和的一半 - 比较左指针和
mid
的大小- 如果左指针小于等于
mid
,证明这个区间是个递增区间,判断mid
是否在这个区间内- 在,将右指针放到
mid
左边一位,也就是mid-1
- 不在,将左指针放到
mid
右一位,也就是mid+1
- 在,将右指针放到
- 如果左指针大于等于
mid
,证明从mid
到右指针区间是递增区间,判断mid
是否在这个区间内- 在,将左指针放到
mid
右一位,也就是mid+1
- 不在,将右指针放到
mid
左边一位,也就是mid-1
- 在,将左指针放到
- 如果左指针小于等于
在循环完成后直接返回-1
就行了,因为这证明没找到target
,直接GG。
整体逻辑还是比较简单的,?看看具体的代码。
自己的答案(二分)
var search = function(nums, target) {
var left = 0
right = nums.length - 1
while (left <= right) {
var mid = ~~((left + right) / 2)
if (nums[mid] === target) return mid
if (nums[left] <= nums[mid]) {
if (nums[left] <= target && nums[mid] > target) {
right = mid - 1
} else {
left = mid + 1
}
} else {
if (nums[right] >= target && nums[mid] < target) {
left = mid + 1
} else {
right = mid - 1
}
}
}
return -1
};
复制代码
这里有几个地方需要注意:
- 不需要特别处理为1的情况,如果数组长度只有1,那么
mid
就会变成这个唯一的元素,接下来不管是left
会自增,然后超过right
,终止循环。 - 数组长度为0的情况也不需要处理,因为题目规定数组长度最少为1。
- 注意处理边界情况,注意区分
>=
和<
,不能都用>=
和<=
。
别的也就没啥了,具体的逻辑在解释部分已经说过了。
更好的方法
无
PS:想查看往期文章和题目可以点击下面的链接:
这里是按照日期分类的?
经过有些朋友的提醒,感觉也应该按照题型分类
这里是按照题型分类的?
有兴趣的也可以看看我的个人主页?