1,题目要求
Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.
(i.e., [0,1,2,4,5,6,7]
might become [4,5,6,7,0,1,2]
).
You are given a target value to search. If found in the array return its index, otherwise return -1.
You may assume no duplicate exists in the array.
Your algorithm’s runtime complexity must be in the order ofO(log n)
.
Example 1:
Input: nums = [4,5,6,7,0,1,2],target = 0
Output: 4
Example 2:
Input:nums = [4,5,6,7,0,1,2], target = 3
Output: -1
假设按升序排序的数组在事先未知的某个枢轴处旋转。
您将获得要搜索的目标值。 如果在数组中找到则返回其索引,否则返回-1。
您可以假设数组中不存在重复。
算法的运行时复杂度必须是O(log n)
的顺序。
2,题目思路
对于这道题,是找到一个经过旋转的有序数组内查找给定元素。
对于元素查找,一定可以在O(n)的时间内完成查找。但是题目要求在O(logn)的时间内完成,如果是二分查找,而自然是可以的。
但是,二分查找有要求,即数组一定是有序的。
题目中给出的数组是经过旋转的数组,详见题目:
189. Rotate Array
对这种类型的数组而言,我们经过分析可以得到,它其实是由两个有序的数组所组成的。如果我们能够找到二者的分界位置,自然就可以使用二分查找的办法来查找元素了。
其中,对于这个分界位置的查找,因为都是部分有序的,基于时间复杂度的考量,我们还是会使用二分的策略来实现。
总体说来,问题的实现步骤为:
- 利用二分查找找到数组中的最小的元素,即两个有序数组的分界位置。
- 根据分界位置的下标,找到偏移量,基于偏移量再次使用二分查找,对target进行查找。
3,代码实现
int x = []() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
return 0;
}();
class Solution {
public:
int search(vector<int>& nums, int target) {
//利用二分策略,找到nums中最小的元素的下标
//即两个有序序列的中间分界处
//这里需要注意的是,之所以不是low<=high然后high=mid-1,
//是因为我们需要找的是最小的那个,也就是后半部分的开头
//最后搜索肯定会落在[max, min, min+1,...min+k]内,
//且low = max.index, high = (min+k).index
//而整数除法的特性是取整,因此,如果按照普通二分的策略
//最后因为high-1和low<=high,肯定会落到max.index上,这不是我们想要的
//使用下面的判断标准,我们最后会有:[max,min]的序列
//之后,根据mid的计算,以及条件判断,可以得low = mid+1
//即最后low所表示的下表就是我们想要求得下标
int n = nums.size();
if(n == 0)
return -1;
int low = 0, high = n-1;
while(low < high){
int mid = low + (high - low)/2;
if(nums[mid] > nums[high])
low = mid+1;
else
high = mid;
}
//利用取余的方法,获得原数组在有序状态下的真正中心点
//省去对原数组分块查找或者重排的烦恼
int rotateIndex = low;
low = 0, high = n-1;
while(low <= high){
int mid = low + (high - low)/2;
int realMid = (mid + rotateIndex)%n;
if(nums[realMid] == target)
return realMid;
else if(nums[realMid] < target)
low = mid+1;
else
high = mid-1;
}
return -1;
}
};