81. 搜索旋转排序数组 II
题目描述
假设按照升序排序的数组在预先未知的某个点上进行了旋转。(例如,数组[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
可能包含重复元素。这会影响到程序的时间复杂度吗?会有怎样的影响,为什么?
思路
根据mid
位于前半段还是后半段,mid
的划分会造成局部单调性,目标在这个单调区间中,搜索转向这个区间。目标不在这个单调范围中,则可以排除这个单调区间,搜索转向另半个区间
如果这个条件成立:nums[l]==nums[mid] && nums[mid]==nums[r]
,则我们无法判断mid到底是位于前半段,还是后半段,我们可以举出这种情况下,mid在前半段实际例子,又可以举出在后半段的实际例子。
如果这个条件不成立,则我们可以判断mid到底是位于前半段还是后半段。论证过程可以使用反证法进行证明。例如,if(nums[mid]<=nums[r])
,我们可以得出右边有序,mid位于后半段,此时假如mid位于前半段,则:
nums[mid]>=nums[r]
(前半段的元素都大于后半段的元素),而if(nums[mid]<=nums[r])
,所以nums[mid]==nums[r]
.- 而假如mid位于前半段,则此时前半段有序,
nums[mid]>=nums[l]
,而nums[l]>=nums[r]
(前半段的元素都大于后半段的元素),所以nums[mid]>=nums[l]>=nums[r]
,而nums[mid]==nums[r]
,所以nums[mid]==nums[l]==nums[r]
,矛盾.
这是在整个数组还是存在两个不同的有序子数组的情况下,程序逻辑无误。当已经为一个单独的有序数组时,if(nums[mid]<=nums[r])
,我们仍然可以得出右边有序,然后进行下面的排查这个有序范围的操作。
总之,无论整个数组是存在两个不同的有序子数组的情况,还是已经是单独的有序数组的情况,我们都可以进行这个排查有序范围的操作。
代码
class Solution {
public:
bool search(vector<int>& nums, int target) {
int n = nums.size();
int l=0;
int r=n-1;
while(l<=r)
{
int mid=l+(r-l)/2;
if(nums[mid]==target)
return true;
//如果这个条件成立,则我们无法判断mid到底是位于前半段,还是后半段,所以
//为了后面算法的设施,需要首选进行判断,排除
if(nums[l]==nums[mid] && nums[mid]==nums[r])
{
l++;
r--;
//因为经过上面的判断A[mid]!=target,所以A[l],A[r]都可以从搜索范围中排除掉
}
else//下面的情况是能够确定mid位于旋转数组的那一段的情况
{
if(nums[mid]<=nums[r])//右边有序
{
if(nums[mid]<target && target<=nums[r])
l=mid+1;
else
r=mid-1;//排除掉
}
else if(nums[l]<=nums[mid])//左边有序
{
if(nums[l]<=target && target<nums[mid])
r=mid-1;
else
l=mid+1;//排除掉
}
}
}
return false;
}
};