$$\color{Red}{搜索旋转排序数组【leetcode33 二分】}$$
我做的所有的题解
包括基础提高以及一些零散刷的各种各样的题
题目介绍
整数数组 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
。
你必须设计一个时间复杂度为 O(log n)
的算法解决此问题。
示例 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
-104 <= nums[i] <= 10^4
nums
中的每个值都 独一无二- 题目数据保证
nums
在预先未知的某个下标上进行了旋转 -104 <= target <= 10^4
解析
其实这道题论证了,只要具有两段性就可以使用二分,并非必须具有单调性。
我们可以先观察一下,数组旋转之后的样子 : 【以 [0, 1, 2, 5, 6] 为例子】
6
5
2
1
0
index 0 1 2 3 4
旋转之后
6
5
2
1
0
index 0 1 2 3 4
我们发现分段之后两段都各自遵循单调性,如果我们能找到这两段的分界线,满足前一段满足一个性质,后一段也能满足,其实无须单调性也能二分。
其实很轻松就能看出来,分段之后后一段的最大值势必小于第一段的最小值即 nums[0]
【原数组无重复元素且单调】
那么就可以以 是否大于等于 nums[0]
作为分界条件,最后将两段的边界找出。
找到满足等于 target
的下标,也可以利用如上性质,直接判断是否大于等于 nums[0]
,大于只能在第一段,小于就在第二段。
这次用是否大于等于 target
作为二分条件即可。
坑
这道题有个坑,就是如果是不存在的情况下,且 target
理论可能存在第二段的情况下,我们会先进行边界的移动, l = l = 1
, r = nums.length - 1
。
但是有可能出现越界,如 [1] , target = 0
,最后 l = 1, r = 0
,如果最后返回的时候用 nums[l]
,就越界了,这里一定要选择 nums[r]
,保证无法越界。其他会改变下标的情况也是如此,先使用保证不越界的可以少判断很多边界情况
class Solution {
public int search(int[] nums, int target) {
if (nums == null) return -1;
int l = 0, r = nums.length - 1;
while (l < r) {
int mid = l + r + 1 >> 1;
if (nums[mid] >= nums[0]) l = mid;
else r = mid - 1;
}
if (target >= nums[0]) l = 0;
else {
l = l + 1;
r = nums.length - 1;
}
while (l < r) {
int mid = l + r >> 1;
if (nums[mid] < target) l = mid + 1;
else r = mid;
}
return nums[r] == target ? r : -1;
}
}