题目描述
二分法
思路:根据旋转排序数组的特点,比如[4,5,6,7,0,1,2]
,可以发现一个规律:在分割点之前、分割点之后的两部分数组,左边的所有元素都比右边的大。所以,我们就根据这个进行二分搜索,找到那个分割点!
二分搜索查找分割点的思路:可以以nums的最后一个数作为参考,比如[4,5,6,7,0,1,2]
,最后的一个数是2,分割点左边[4,5,6,7]
一定都比2大,分割点右边[0,1,2]
一定都小于等于2,所以用二分查找在nums中找出第一个小于等于2的位置即可。
拿到分割点之后,再判断target和nums最后一个数的关系,如果target较大,那么它只可能出现在分割点左边,在分割点左边的那一部分进行二分搜索即可,反之,在右边搜索。
class Solution {
public int search(int[] nums, int target) {
//先找到分割点
int l = 0,r = nums.length - 1;
int mid;
while(l < r && nums[l] > nums[r]){
mid = (l + r) >> 1;//取l和r中间的那个数
if(nums[mid] > nums[l]){
l = mid + 1; //如果nums[mid]>nums[l],那么mid和l一定都在分割点之前,直接让l=mid+1
}else{
++l; //否则的情况是mid在分割点之后,l在分割点之前
//这里不能让l=mid,因为我们要找的是分割点之后的第一个位置,此时mid不一定是
}
}
int spilt = l; //分割点是l
int res;
if(nums[nums.length - 1] < target){
//target比分割点右边的所有的数都大,那就从左边找
res = Arrays.binarySearch(nums,0,spilt,target);
}else{
//否则,从右边找
res = Arrays.binarySearch(nums,spilt,nums.length,target);
}
return Math.max(-1,res);
}
}
关联题目 No81. 搜索旋转排序数组 II
题目描述
思路
这道题和上面那一道题的唯一的区别就是这道题有可能有重复元素,也就是说nums=[0,1,2,3,3,3,3,4,5,6,7]
在某一位置处截断并旋转之后可能会出现[3,3,4,5,6,7,0,1,2,3,3]
的情况,
如果是上面那道题的情况的话,如果数组[4,5,6,7,0,1,2,3]
的[4,5,6,7]
这一部分是严格大于3的,而[0,1,2,3]
是小于等于3的,可以通过二分搜索找到第一个小于等于3的位置,
但是对这道题而言,如果出现数组是[3,3,4,5,6,7,0,1,2,3,3]
,那么左边的部分[3,3,4,5,6,7]
是大于等于3的,右边部分[0,1,2,3,3]
是小于等于3的,两边都有可能取等号,所以不好用二分搜索来做!
我的思路是在拿到nums数组之后,首先在数组结尾去掉重复项。也就是说[3,3,4,5,6,7,0,1,2,3,3]
这个数组,我首先预处理一下,将末尾重复的3都给去掉,使其变成[3,3,4,5,6,7,0,1,2]
,这样子就跟一开始的那道题目一样了!
代码如下:
class Solution {
public boolean search(int[] nums, int target) {
int l = 0;
int r = nums.length- 1;
//直接跳过首尾相同的部分,使它和第33题相同
while(r > 0 && nums[r] == nums[0]){
--r;
}
//先找到分割点
int mid;
while(l < r && nums[l] > nums[r]){
mid = (l + r) >> 1;
if(nums[mid] > nums[l]){
l = mid + 1;
}else{
++l;
}
}
int spilt = l; //分割点是l
int res;
if(nums[r] < target){ //33题里面是nums[nums.length-1],这里是nums[r],因为这道题相当于从后面截断数组。
//target比分割点右边的所有的数都大,那就从左边找
res = Arrays.binarySearch(nums,0,spilt,target);
}else{
//否则,从右边找
res = Arrays.binarySearch(nums,spilt,r+1,target);
}
return res >= 0;
}
}