1. 题目描述
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,0,1,2,2,5,6] 可能变为 [2,5,6,0,0,1,2] )。
编写一个函数来判断给定的目标值是否存在于数组中。若存在返回 true,否则返回 false。
示例 1:
输入: nums = [2,5,6,0,0,1,2], target = 0
输出: true
示例 2:
输入: nums = [2,5,6,0,0,1,2], target = 3
输出: false
进阶:
- 这是 搜索旋转排序数组 的延伸题目,本题中的 nums 可能包含重复元素。
- 这会影响到程序的时间复杂度吗?会有怎样的影响,为什么?
2. 二分法 & 双指针(Binary Search & Two Pointers)
2.1 解题思路
本体是33. Search in Rotated Sorted Array(搜索旋转排序数组)一道相关联的题目,和上一题不同的是:这次数组会有重复数字。
所以这里笔者只总结和讲解重点的部分,一些细节的地方可以参照上一题:33. Search in Rotated Sorted Array(搜索旋转排序数组)两种解法(C++ & 注释)
首先我们来回顾一下上一题所涉及的典型例子:
- 旋转后形成峰顶和谷底:[0,1,2,4,5,6,7] -> [4,5,6,7,0,1,2];
- 旋转后形成降序:[1,3] -> [3,1];
- 旋转后升序不变:[1,3] -> [1,3];
- 空数组:[] -> [];
本题自然也包含上述这些情况,只是有一种非常特殊的情形:旋转后形成峰顶和谷底,也两边界会相等:[0,0,1,2,2,5,6] -> [2,5,6,0,0,1,2],这种情形使用上一题的思路还是OK的,应该mid取不到两边相等的数,所以木有问题,但是对于下面这种情况就GG了:
[4,4,4,4,4,7,4,4]
所以我们不能向上题那样,判断nums[mid]和nums[left]和nums[right]的大小关系来进行递归,因为峰顶可能存在于两边,所以将计就计,分别查找两边是否有峰顶,找得到则返回序号,找不到返回-1。找到峰顶序号以后的操作和上题一致,依次对三种(完全升序,峰顶左边,峰顶右边)情况进行分别查找即可。
嘛,最后发现本题简化后的代码完全适用上一题哒,也算是做了一下优化吧。到此也回答了follow up里面的问题:和上题相比,有重复数字会影响时间复杂度么?答案是不会哒,都是O(logn),而且代码和思路都是通用的,只是上一题是这题的一个特例。
最后说一下binary_search(first, last, target)函数的用法:这个函数是在[first, last)范围内二分查找target,找到了返回true,反之返回false;first 和 last为迭代器。
2.2 实例代码
class Solution {
bool twoPointers(vector<int>& nums, int left, int right, int target) {
while (left <= right) {
if (nums[left] == target) return true;
if (nums[right] == target) return true;
left++;
right--;
}
return false;
}
int findPeak(vector<int>& nums, int left, int right) {
if (left > right) return -1;
int mid = (right - left) / 2 + left;
// 找到峰顶
if (nums[mid - 1 >= 0 ? mid - 1 : 0] <= nums[mid] && nums[mid] > nums[mid + 1 >= nums.size() ? nums.size() - 1 : mid + 1]) return mid;
// 没找到峰顶,两边继续寻找
int peakLeft = findPeak(nums, left, mid - 1), peakRight = findPeak(nums, mid + 1, right);
return peakLeft == -1 ? (peakRight == -1 ? -1 : peakRight) : peakLeft;
}
public:
// twoPointers和binary_search二选一即可
bool search(vector<int>& nums, int target) {
int len = nums.size(), peak = findPeak(nums, 0, len - 1);
if (peak == -1) { // 完全升序
// return twoPointers(nums, 0, len - 1, target);
return binary_search(nums.begin(), nums.end(), target);
}
else if (nums[0] <= target && target <= nums[peak]) { // 峰顶左边
// return twoPointers(nums, 0, peak, target);
return binary_search(nums.begin(), nums.begin() + peak + 1, target);
}
else if (nums[peak + 1] <= target && target <= nums[len - 1]) { // 峰顶右边
// return twoPointers(nums, peak + 1, len - 1, target);
return binary_search(nums.begin() + peak + 1, nums.end(), target);
}
return false;
}
};