整数数组 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) 的算法解决此问题。
目录
思路一:循环(时间超限)
虽然减小了所要循环的数的规模,但时间复杂度还是没达到要求
int search(int* nums, int numsSize, int target){
if(target>nums[0])
{
int i = 1;
while(nums[i]<nums[i+1])
{
for(;i<numsSize/2;i++)
{
if(target==nums[i])return i;
}
i += i/2;
}
return -1;
}
else if(target<nums[0])
{
int i = numsSize-1;
while(nums[i]>nums[i-1])
{
for(;i>numsSize/2;i--)
{
if(target == nums[i])return i;
}
i -= i/2;
}
return -1;
}
else return 0;
}
时间复杂度O(n),空间复杂度O(1)
思路二:二分法
利用两个指针不断缩小范围,使时间复杂度达到O(logn)
int search(int* nums, int numsSize, int target){
int low = 0, high = numsSize - 1;
while(low <= high){
int mid=(high - low) / 2 + low;
if(nums[mid] == target){
return mid;
}
if(nums[low] <= nums[mid]){
if(target >= nums[low] && target <nums[mid])
high = mid - 1;
else
low = mid + 1;
}
else{
if(target > nums[mid] && target <= nums[high])
low = mid + 1;
else
high = mid - 1;
}
}
return -1;
}
时间复杂度O(logn),空间复杂度O(1)
做题历程:
一开始想到因为旋转了数组,可以通过判断目标数与第一个数的大小来决定是从前往后还是从后往前循环看是否相等,同时让比较的区域通过看该位置前一个与后一个比较来判断是否已到选择数的位置来决定返回-1,该方法虽然可行但是时间复杂度上还是有所欠缺,为O(n),提交时时间超过限制,之后想到使用二分法,将数组两边用指针来不断缩小范围进行查找,将时间复杂度缩小到了O(logn)
反省:
问题:
时间复杂度没有降低到O(logn)
收获:
对二分法的应用更加熟练,对于是否需更改边界的条件更加明确
总结:
该题主要考察了二分法的应用,对于时间复杂度要求较高的问题,可尝试二分法来降低时间复杂度