33. Search in Rotated Sorted Array**
https://leetcode.com/problems/search-in-rotated-sorted-array/
题目描述
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 of O ( l o g n ) O(log n) O(logn).
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
解题思路
使用二分法无疑, 然后需要首先找到分界点. 有两点可以明确:
- 分界点
idx
存在nums[idx - 1] > nums[idx]
这个关系; - 在分界点之后的元素都是小于
nums[0]
的.
确定分界点之后, 整个序列就可以化为两个有序序列, 然后根据 target
与 nums[0]
的大小比较, 来选择合适的序列使用二分查找.
对于分界点的查找可以使用二分法, 找到第一个小于 nums[0]
的数.
C++ 实现 1
函数 findPivot
用来查找分界点, 即序列中第一个小于 nums[0]
的数, 如果最后 i >= nums.size()
, 表明 nums[0]
就是序列中的最小值.
另外还需要注意一个陷阱, 分段查找的前提是数组的确被分成了两个部分. 但如果数组本身是有序的, 那就直接用二分查找. 时间复杂度为
O
(
l
o
g
N
)
O(logN)
O(logN)
class Solution {
private:
int findPivot(const vector<int> &nums) {
int i = 0, j = nums.size() - 1;
while (i <= j) {
int mid = i + (j - i) / 2;
// 找到第一个小于 nums[0] 的数
if (nums[mid] >= nums[0]) i = mid + 1;
else j = mid - 1;
}
return i >= nums.size() ? 0 : i;
}
int binarySearch(const vector<int> &nums, int start, int end, int target) {
int i = start, j = end;
while (i <= j) {
int mid = i + (j - i) / 2;
if (nums[mid] == target) return mid;
else if (nums[mid] < target) i = mid + 1;
else j = mid - 1;
}
return -1;
}
public:
int search(vector<int>& nums, int target) {
if (nums.empty()) return -1;
int pivot = findPivot(nums); // 第一个小于 nums[0] 的数的索引
// 如果 pivot == 0, 表明此时只有一个有序序列, 只有 (pivot != 0 && target >= nums[0])
// 两个条件同时成立, 才需要分段查找.
if (pivot != 0 && target >= nums[0]) return binarySearch(nums, 0, pivot - 1, target);
else return binarySearch(nums, pivot, nums.size() - 1, target);
}
};
C++ 实现 2
思路: 使用二分查找, 关键在于边界的确定. (参考了 leetcode-cpp.pdf
上 2.1.3
的解答)
要分情况讨论, 当访问 nums[mid]
时, 考虑两种情况, nums[mid]
是大于或等于 nums[left]
呢, 还是小于 nums[left]
. 在这两种情况下, 又要考虑 target
和 nums[mid]
以及 nums[left]
的关系.
class Solution {
public:
int search(vector<int>& nums, int target) {
if (nums.empty())
return -1;
int l = 0, r = nums.size() - 1;
while (l <= r) {
int mid = l + (r - l) / 2;
if (nums[mid] == target)
return mid;
else if (nums[mid] >= nums[l]) { // 说明 nums[l...mid] 是排序好的
if (target >= nums[l] && target < nums[mid])
r = mid - 1;
else
l = mid + 1;
}
else {// 说明 nums[mid...r] 是排序好的
if (target < nums[l] && target > nums[mid])
l = mid + 1;
else
r = mid - 1;
}
}
return -1;
}
};
C++ 实现 3
将 C++ 实现 2
中的代码做些修改, 改成下面这样也可以:
class Solution {
public:
int search(vector<int>& nums, int target) {
if (nums.empty()) return -1;
int l = 0, r = nums.size() - 1;
while (l <= r) {
int mid = l + (r - l) / 2;
if (nums[mid] == target) return mid;
else if (nums[mid] >= nums[l]) { // 说明 nums[l...mid] 是排序好的
if (target >= nums[l] && target < nums[mid])
r = mid - 1;
else
l = mid + 1;
}
else {// 说明 nums[mid...r] 是排序好的
if (target > nums[mid] && target <= nums[r])
l = mid + 1;
else
r = mid - 1;
}
}
return -1;
}
};