引用自:LeetCode-中等-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)
的解决方案吗?
没什么好说的,主要分两步
首先用二分法查找出K是多少
设置查找范围left,right
,最开始从数组的两端开始,也就是left = 0
, right = nums.length - 1
。判断范围的中间点的值nums[mid]
(其中mid = (left + right) / 2
)大于nums[left]
还是小于nums[left]
,当大于时,说明序列的开头位置在mid
的后边,则将left = mid
,即将查找范围缩小在mid
与right
之间。当小于时,说明整个序列序列的最小值的开端在mid
的前边,则将right
= mid
,即将查找范围缩小在left
与mid
之间。循环执行查找,直到范围缩小到2,即left = right - 1
。此时right
位置,即为整个升序列的开端位置,但存在特殊情况,如序列[1, 2]
,当查找到left = 0
,right = 1
的时候,按照上述方法则序列开端在right = 1
,即nums[1] = 2
处,但此时正确旋转位置为0,为了解决此情况,当left = right - 1
时,若nums[left] > nums[right]
则时正常情况,返回K=nums.length - right
若nums[left] < nums[right]
时,为特情况,特殊情况下,返回K=0
;
时间复杂度为:O(log n)
用二分法查找target值下标
由于已知K
,则算出来相对升序列的头部(最小值)在哪,若不旋转,则该值应该位于下标为0的位置,而按K
旋转后,则下标发生偏移,记偏移量offSet = nums.length - K
。
接下来只需要按照正常的二分查找去寻找,并且将下标进行偏移转换即可(下标 X' = X + offSet
),同时需要注意,偏移之后的下标可能越界,每次用到nums[ X' ]
时,只需用nums[ X' % nums.length]
即可。
时间复杂度为:O(log n)
//Java 提交leetcode的
class Solution {
public int search(int[] nums, int target) {
if (nums.length == 1) {
return nums[0] == target ? 0 : -1;
}
int offSet = nums.length - findK(nums, 0, nums.length - 1);
int left = 0 + offSet;
int right = nums.length - 1 + offSet;
while (left <= right) {
int mid = (left + right) >> 1;
if (nums[mid % nums.length] == target) {
return mid % nums.length;
}
if (nums[mid % nums.length] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return -1;
}
public int findK(int[] nums, int left, int right) {
if (right - left == 1) {
return nums[left] > nums[right] ? nums.length - right : 0;
}
if (nums[(right + left) >> 1] < nums[left]) {
return this.findK(nums, left, (right + left) >> 1);
} else {
return this.findK(nums, (right + left) >> 1, right);
}
}
}
//Java
class Solution33 {
public int search(int[] nums, int target) {
if (nums.length == 1) {
return nums[0] == target ? 0 : -1;
}
int offSet = nums.length - findK(nums, 0, nums.length - 1);
int left = 0 + offSet;
int right = nums.length - 1 + offSet;
while (left <= right) {
int mid = (left + right) >> 1;
if (nums[mid % nums.length] == target) {
return mid % nums.length;
}
if (nums[mid % nums.length] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return -1;
}
public int findK(int[] nums, int left, int right) {
if (right - left == 1) {
return nums[left] > nums[right] ? nums.length - right : 0;
}
if (nums[(right + left) >> 1] < nums[left]) {
return this.findK(nums, left, (right + left) >> 1);
} else {
return this.findK(nums, (right + left) >> 1, right);
}
}
public static void main(String[] args) {
Solution33 solution33 = new Solution33();
int[] nums = new int[]{4, 5, 6, 7, 0, 1, 2};
System.out.println("测试1:");
System.out.println("输入:nums = [4,5,6,7,0,1,2], target = 0");
System.out.println("输出:" + solution33.search(nums, 0));
System.out.println("\n测试2:");
System.out.println("输入:nums = [4,5,6,7,0,1,2], target = 3");
System.out.println("输出:" + solution33.search(nums, 3));
}
}