【每日算法】LeetCode 33 —— 搜索旋转排序数组 (一百一二十五)

题目内容

整数数组 nums 按升序排列,数组中的值 互不相同 。

在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。

给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。

进阶:你可以设计一个时间复杂度为 O(log n) 的解决方案吗?

示例

示例 1:

输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4

示例 2:

输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1

示例 3:

输入:nums = [1], target = 0
输出:-1

提示

1、1 <= nums.length <= 5000
2、-10^4 <= nums[i] <= 10^4
3、nums 中的每个值都 独一无二
4、题目数据保证 nums 在预先未知的某个下标上进行了旋转
5、-10^4 <= target <= 10^4

题解

本题在求解上也是采用二分的思想来做。可以先画个简图方便我们理解。

然后,我们根据图,开始思考本题的思路

1、二分的基本思想就是某个分界点将区间分为两段,一个区间内满足某种性质,一个区间不满足,找出满足区间的那一段并更新为下一次求解的区间,在该区间内继续寻找分界点,重复上述操作,直到区间被更新为一个点或者左边界大于右边界时停止。

2、在本题中,我们需要找到target的下标。因此,针对本题来说,首先要找到两个区间的分界点,也就是k的下标,将两个旋转的区间分开,也是同样采用二分的方式,去判断区间的值与nums[0]的大小关系,一段区间均大于等于,一段均小于。这个查找完了之后,二分的左右端点相等,等于k。

3、然后我们要判断target在哪一段区间内。直接判断target与nums[k]的关系,确定target属于的区间范围,然后更新二分算法的左右端点。

3、判断出target在哪个区间之后,由于之后的区间不需要考虑旋转的问题,所以继续使用普通的二分做就可以了。本题主要是第一次的二分,需要特殊判断一下即可。

接下来,请看代码以及注释。

代码

class Solution {
public:
int search(vector<int>& nums, int target) {
//根据遍历的x是否大于等于nums[0]来讲旋转的点找出来。
//同样采用二分来做
int l = 0, r = nums.size() - 1;//初始化二分的左右边界
while(l < r){
//求k
int mid = l + r + 1 >> 1;//取区间中点。右移运算符移动一位,相当于/2
if(nums[mid] >= nums[0]) l = mid;
else r = mid - 1;
}
//更新二分区间端点
if(target >= nums[0]) l = 0;
else l = r + 1, r = nums.size() - 1;
//二分查找target
while(l < r){
int mid = l + r >> 1;
if(nums[mid] >= target){
r = mid;
}else l = mid + 1;
}
//返回结果
if(nums[r] == target) return r;
else return -1;
}
};
Author: Frederic Niu
Link: https://www.fredericniu.cn/2021/04/28/【每日算法】LeetCode-33-——-搜索旋转排序数组-(一百一二十五)/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
我的公众号