1. 题目描述
[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) 的解决方案吗?
Related Topics 数组 二分查找
2. 思路分析
这道题目与正常的二分查找区别在于,存在一个节点之后的值,都比之前的值小。
提取一下关键点,这个节点之后的值,都小于 nums[0]。我们一开始就可以把目标值与 nums[0]做比较,可以得到目标值在旋转节点左边和右边。根据这个,我们来做二分查找。有四种情况
num[mid] < target的情况下nums[mid] < num[0]并且 目标值在旋转节点左边,那么目标值肯定在mid下标的左边,即 high = mid - 1;num[mid] < target的情况下除1以外的情况,都与正常二分一样处理- 类似1,2。
num[mid] > target的情况下,如果nums[0] > nums[mid]或者 选节点在左边,那么目标值肯定在mid下标的左边,即 high = mid - 1; num[mid] > target的情况下,除了3以外的情况,都给正常二分不同,即 low = mid + 1
这道题的主要是把上面几个情况分开,道理我都是懂的,就是调的时候有一堆毛病....
3. AC代码
public class No33 {
public int search(int[] nums, int target) {
return method1(nums, target);
}
private int method1(int[] nums, int target) {
int low = 0;
int high = nums.length - 1;
// 判断目标值在旋转值左边还是右边
boolean targetInLeft = nums[0] <= target?true:false;
while (low <= high) {
// 找到中间值
int mid =low + ((high-low)>>1);
if(nums[mid] == target) {
return mid;
} else if (nums[mid] < target) {
if(targetInLeft) {
// 目标在旋转点左边,且在mid之前出现了旋转点
if(nums[0] > nums[mid]) {
high = mid -1;
} else {
low = mid + 1;
}
} else {
low = mid + 1;
}
} else {
// 如果目标在旋转点左边,且 nums[mid] > target
if(targetInLeft) {
high = mid -1;
} else {
if(nums[0] > nums[mid]) {
high = mid - 1;
} else {
low = mid + 1;
}
}
}
}
return -1;
}
// 把method1该稍微好看一点点
private int method2(int[] nums, int target) {
int low = 0;
int high = nums.length - 1;
// 判断目标值在旋转值左边还是右边
boolean targetInLeft = nums[0] <= target?true:false;
while (low <= high) {
// 找到中间值
int mid =low + ((high-low)>>1);
if(nums[mid] == target) {
return mid;
} else if (nums[mid] < target) {
// 目标在旋转点左边,且在mid之前出现了旋转点,, 目标值都在左边
if(targetInLeft && nums[0] > nums[mid]) {
high = mid -1;
} else {
low = mid + 1;
}
} else {
// 如果目标在旋转点左边 或者 nums[mid] > target , 目标值都在左边
if(targetInLeft || nums[0] > nums[mid]) {
high = mid - 1;
} else {
low = mid + 1;
}
}
}
return -1;
}
}
4. 总结
这道题也对应<极客时间-数据结构与算法之美>二分查找的课后练习,推荐大家去看