题目描述
整数数组 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 <= nums.length <= 5000 -10^4 <= nums[i] <= 10^4 nums 中的每个值都 独一无二 题目数据保证 nums 在预先未知的某个下标上进行了旋转 -10^4 <= target <= 10^4
题解
解题思路
思路一(二分查找):
- 因数组是通过旋转得到,所以存在两个升序部分(也可能只存在一个)。我们采用二分查找的方法,查找到 mid 中间坐标,此时 mid 左右两侧必定是一侧有序,一侧无序(左侧包含 mid,右侧区间包含 mid)(有序也可看做无序处理)。
- 首先判断 nums[mid] 是否等于 target,若相等则直接返回。
- 若 nums[mid] != target。可通过 nums[mid] 与 nums[left] 的比较,来判断左侧区域是否有序。
- nums[mid] >= nums[left](注意这里是>=,当存在两个元素时 left = mid。如 [1,2],target=2)左侧有序,右侧无序,则需判断 target 是否在 nums[left]~nums[mid] 的区间内。
- 若 target 在区间内,则在左区间(有序区间)继续进行查找(此时为普通的二分查找):right = mid-1
- 若 target 不在区间内,则在右区间(无序区间)继续进行查找:left = mid+1
- nums[mid] <= nums[right] 左侧无序,右侧有序,则需判断 target 是否在 nums[mid]~nums[right] 的区间内。
- 若 target 在区间内,则在右区间(有序区间)继续进行查找(此时为普通的二分查找):left = mid+1
- 若 target 不在区间内,则在左区间(无序区间)继续进行查找:right = mid-1
- nums[mid] >= nums[left](注意这里是>=,当存在两个元素时 left = mid。如 [1,2],target=2)左侧有序,右侧无序,则需判断 target 是否在 nums[left]~nums[mid] 的区间内。
- 若 left > right 则查找失败。
例:nums={4,5,6,7,0,1,2};target=0; 初始:left = 0, right = 6, target = 0,中间元素 nums[mid] = 7,不等于目标值,左侧有序,target 不在左侧区间,更新 left = 4。 第二次:left = 4, right = 6,中间元素 nums[mid] = 1,不等于目标值,左侧有序,target 在左侧区间,更新 right = 4。 第三次:left = 4, right = 4,中间元素 nums[mid] = 0,等于目标值,返回索引 4。
- 复杂度分析:
- 时间复杂度:O(logn),其中 n 为 nums 数组的大小。整个算法时间复杂度即为二分查找的时间复杂度 O(logn)。
- 空间复杂度:O(1)。
代码实现
代码实现(思路一(二分查找)):
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
int mid;
while (left <= right) {
// 计算中间元素的下标
mid = left + (right - left) / 2;
// 如果中间元素为目标值 target 则返回中间元素的下标
if (nums[mid] == target) return mid;
// 判断左侧区间是否有序
if (nums[left] <= nums[mid]) {
// 若左侧有序,则判断 target 是否存在左侧区间范围内,存在则查找左侧区间,否则查找右侧区间
if (nums[left] <= target && target < nums[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
} else {
// 若右侧区间有序,则判断 target 是否存在右侧区间范围内,存在则查找右侧区间,否则查找左侧区间
if (nums[mid] < target && target <= nums[right]) {
left = mid + 1;
} else {
right = mid - 1;
}
}
}
// 如果查找不到 target 则返回 -1
return -1;
}
};
调试示例
#include<iostream>
#include<vector>
using namespace std;
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
int mid;
while (left <= right) {
mid = left + (right - left) / 2;
if (nums[mid] == target) return mid;
if (nums[left] <= nums[mid]) {
if (nums[left] <= target && target < nums[mid]) {
right = mid - 1;
} else {
left = mid + 1;
}
} else {
if (nums[mid] < target && target <= nums[right]) {
left = mid + 1;
} else {
right = mid - 1;
}
}
}
return -1;
}
};
int main(int argc, char const* argv[]) {
vector<int> nums = {4,5,6,7,0,1,2};
int target = 0;
// 旋转排序数组
Solution s;
cout << s.search(nums, target);
return 0;
}

