Search in Rotated Sorted Array(旋转后的有序数组中的搜索)
【难度:hard or Easy】
Suppose a sorted array 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.
假设一有序数组在你不知道的情况下绕某一点旋转,如[0,1,2,4,5,6,7]旋转后变为了[4,5,6,7,0,1,2]。给定一个目标值,如果在旋转后的数组中能够找到,则返回其下标,否则返回-1。
解题思路
一看到有序数组,大家很自然就想到使用二分查找的方法,快速有效。但是旋转后的数组顺序已经改变了,常规的二分查找不能奏效。通过观察,我们知道原来全体有序的数组在旋转后变为了两个部分内部有序,因此如果对二分查找的方法进行一些调整,还是可以实现查找功能的。
我来举个例子:
假如有这样旋转后的数组为
[12, 13, 14, 15, 16, 17, 18, 19, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
如果目标值为14, 那么我们可以把数组理解为下面的样子:
[12, 13, 14, 15, 16, 17, 18, 19, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX]如果目标值为 7, 则将数组理解为下面的样子:
[INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, INT_MIN, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
那么对于上面的情况,使用常规的二分查找是可以实现的。
但是我们在数组情况未知下,不是真的这样去修改数组内容,而是在运行时动态地更改我们看到的数据的样子。
如何更改?关键在于这几行代码:
int mid = (low+high)/2;
int num = (nums[mid] < nums[0]) == (target < nums[0]) ? nums[mid] : target < nums[0] ? INT_MIN : INT_MAX;
if (num < target)
low = mid+1;
else if (num > target)
high = mid;
else
return mid;
这段代码的作用是,判断中点的值nums[mid]与目标值target是否都在nums[0]的同一边(同true或同false):
1)是,则取num = nums[mid],根据num和target的大小关系来判断target可能位于的区间;
2)否,则进一步判断target < nums[0]是否成立:
如果成立,则说明nums[mid] >= nums[0],区间[0,mid]是升序区间,target不可能存在这个区间内,将num设置为INT_MIN,从而在下一个判断语句中将下边界变为mid+1;
如果不成立,则说明target >= nums[0] && nums[mid] < nums[0]为true,那么区间[mid,size-1]内的所有数小于target,将num设置为INT_MAX,从而在下一个判断语句中将上边界变为mid(而不是mid-1,因为mid-1还未与target比对)
c++代码如下:
class Solution {
public:
int search(vector<int>& nums, int target) {
if (nums.empty())
return -1;
int low = 0;
int high = nums.size();
while (low < high) {
int mid = (low+high)/2;
int num = (nums[mid] < nums[0]) == (target < nums[0]) ? nums[mid] : target < nums[0] ? INT_MIN : INT_MAX;
if (num < target)
low = mid+1;
else if (num > target)
high = mid;
else
return mid;
}
return -1;
}
};
但是,除了二分查找的方法外,有个简单又粗暴的解法——遍历查找!
可能这道题的样例组数不够大,我使用上面的方法和遍历查找的方法耗时都在4ms,但是遍历查找无疑是最最直接,代码量还小。
遍历c++代码如下:
class Solution {
public:
int search(vector<int>& nums, int target) {
if (nums.empty())
return -1;
int high = nums.size();
for (int i = 0; i < high; i++)
if (nums[i] == target)
return i;
return -1;
}
};