一、二分
最常见的二分就是用在搜索有序数组中的元素了,这会让人有个错觉:只有数组是有序的,才能使用二分。
其实是这样的:每次能决定丢弃掉哪一边,都可以使用二分
二、题目
整数数组 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 。
三、分析
例如数组是[4,5,6,7,0,1,2] ,数组左边的数是有序的,右边的数也是有序的,且左边的数一定比右边的数大。这道题要求O(logn)的时间复杂度,那就只能用二分了。
每次拿到nums[mid],会出现以下两种情况:
1、mid在左半部分
当mid在左半部分,此时[left - mid]这个区间是有序的,当前需要考虑丢弃掉哪一边。
1)nums[left] <= target < nums[mid],就在左边继续查找,right = mid - 1
2)否则,left = mid + 1
2、mid在右半部分
当mid在右半部分,此时[mid - right]这个区间是有序的,在这个条件下考虑丢弃哪一边
1)nums[mid] < target <= nums[right],就在右边继续查找,left = mid + 1
2)否则在左边查找,right = mid - 1
四、代码
class Solution {
public int search(int[] nums, int target) {
//在i下标处的值是最小的
//二分 O(logn)
//左边部分的值一定比右边部分的大
int left = 0;
int right = nums.length - 1;
while(left <= right){
int mid = (left + right) / 2;
if(nums[mid] == target){
return mid;
}
if(nums[left] <= nums[mid]){
//mid左边是有序的
if(nums[left] <= target && nums[mid] > target) right = mid - 1;
else left = mid + 1;
}else{
//nums[left] > nums[mid]
//mid右边是有序的
if(nums[mid] < target && target <= nums[right]) left = mid + 1;
else right = mid - 1;
}
}
return -1;
}
}