整数数组 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] <= 104
nums
中的每个值都 独一无二nums
在预先未知的某个下标上进行了旋转-104 <= target <= 104
通过次数
785.2K
提交次数
1.8M
通过率
43.9%
在数组中找一个值,我们首先想到的方法就是遍历整个数组,但是这题要求时间复杂度为O(log n),直接遍历肯定是不能了,那我们该怎么办呢?
看到O(log n)的时间复杂度,我们首先想到的就是二分法,但是我们之前用的二分法都是在顺序区间里用的,这是因为我们在使用二分法的时候,都是先确定区间的两个端点的中点的值,然后根据目标值target和nums[left],nums[right],nums[mid]的比较判断target在哪个区间,而只有顺序区间,才能通过端点的值来判断target在不在区间里。
我们来分析以下这个旋转排序数组,它是由两部分升序数组组成的,设为part_A,和part_B,而且左边的那一部分的最小值大于右边的那一部分的最大值。如果我们取数组的中点把数组分为左右两部分设为left,right。我们会发现,不管这中点是在part_A还是在part_B,left和right至少有一个是顺序区间,而令一个要么是有序区间,有么是单调性和旋转排序数组一样的区间。对于那个有序区间,我们可以判断target在不在区间里,如果在,就在那个有序区间里找,如果不在,就去另一个区间里找。而前面提到,另一个区间和旋转排序数组的结构一样,所以我们可以可以同样用取中点的方法来寻找。
综上所述:
搞懂这个题的精髓在于三个点
一:只有在顺序区间内才可以通过区间两端的数值判断target是否在其中。
二:判断顺序区间还是乱序区间,只需要对比 left 和 right 是否是顺序对即可,left <= right,顺序区间,否则乱序区间。
三:每次二分都会至少存在一个顺序区间。
通过不断的用Mid二分,根据定理二,将整个数组划分成顺序区间和乱序区间,然后利用定理一判断target是否在顺序区间,如果在顺序区间,下次循环就直接取顺序区间,如果不在,那么下次循环就取乱序区间。
class Solution {
public://把要搜索的区间分为两部分,对于这两部分,总有一部分完全排序的
int search(vector& nums, int target) {
int lo=0;
int hi=nums.size()-1;
int mid;
while(lo<=hi)
{
mid=(lo+hi)/2;
if(nums[mid]==target) return mid;
if(nums[lo]<=nums[mid])
{//如果左边是顺序区间
//在顺序区间里下次取左边,否则取右边
if(target>=nums[lo]&&targetnums[mid]&&target<=nums[hi])
lo=mid+1;
else
hi=mid-1;
}
}
return -1;
}
};