原题链接🔗:搜索旋转排序数组
难度:中等⭐️⭐️
题目
整数数组 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
- -104 <= nums[i] <= 104
- nums 中的每个值都 独一无二
- 题目数据保证 nums 在预先未知的某个下标上进行了旋转
- -104 <= target <= 104
二分查找
-
二分查找(Binary Search),也称为折半搜索,是一种在有序数组中查找特定元素的搜索算法。其基本思想是将目标值与数组中间的元素进行比较:
- 如果目标值等于中间元素,搜索成功,返回该位置。
- 如果目标值小于中间元素,搜索范围缩小至数组的左半部分。
- 如果目标值大于中间元素,搜索范围缩小至数组的右半部分。
- 这个过程将不断重复,直到找到目标值或者搜索范围为空为止。
-
以下是二分查找算法的一般步骤:
-
初始化:设置两个指针,一个指向数组的起始位置(通常记为
left
),另一个指向数组的结束位置(通常记为right
)。 -
循环条件:当
left
小于等于right
时,继续循环。 -
计算中间位置:计算
left
和right
之间的中间位置mid
,通常使用(left + right) / 2
。 -
比较与调整:
- 如果
nums[mid]
等于目标值,根据需要返回mid
或继续搜索以找到更精确的位置。 - 如果
nums[mid]
大于目标值,将right
调整为mid - 1
。 - 如果
nums[mid]
小于目标值,将left
调整为mid + 1
。
- 如果
-
循环结束:如果
left
大于right
,则表示目标值不在数组中,返回一个特定的值(通常是 -1)表示未找到。
-
-
二分查找的效率非常高,时间复杂度为 O(log n),其中 n 是数组的长度。然而,它要求数组是有序的,并且只能应用于一维有序数组。
-
下面是一个简单的 C++ 实现示例:
int binarySearch(const std::vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
while (left <= right) {
int mid = left + (right - left) / 2; // 防止溢出
if (nums[mid] == target) {
return mid; // 找到目标值
} else if (nums[mid] < target) {
left = mid + 1; // 搜索右半部分
} else {
right = mid - 1; // 搜索左半部分
}
}
return -1; // 未找到目标值
}
这个函数会返回目标值在数组中的索引,如果目标值不在数组中,则返回 -1。
题解
- 解题思路:
确定二分查找的可行性:由于数组是局部有序的,我们可以使用二分查找算法来减少搜索范围。
确定中间元素和边界元素的比较:在二分查找的过程中,我们需要比较中间元素与边界元素,以确定应该在左半部分还是右半部分继续搜索。
处理旋转点:由于数组被旋转,我们需要找到旋转点,即数组中第一个大于或等于中间元素的索引。
二分查找逻辑:
- 如果中间元素等于目标值,返回中间索引。
- 如果左边界元素小于或等于中间元素(即左边是有序的),比较目标值与左边界和中间元素:
- 如果目标值在左边界和中间元素之间,搜索左半部分。
- 否则,搜索右半部分。
- 否则(即右边是有序的),比较目标值与中间元素和右边界:
- 如果目标值在中间元素和右边界之间,搜索右半部分。
- 否则,搜索左半部分。
更新搜索范围:根据上述逻辑,更新左右边界,继续二分查找,直到找到目标值或搜索范围无效。
返回结果:如果找到目标值,返回其索引;否则,返回-1。
- c++ demo:
#include <iostream>
#include <vector>
// 函数声明
int search(const std::vector<int>& nums, int target);
int main() {
// 测试用例
std::vector<int> nums1 = { 4, 5, 6, 7, 0, 1, 2 };
int target1 = 0;
std::cout << "Test 1 - Expected: 4, Found: " << search(nums1, target1) << std::endl;
std::vector<int> nums2 = { 4, 5, 6, 7, 0, 1, 2 };
int target2 = 3; // 注意,这里的目标值不在数组中
std::cout << "Test 2 - Expected: -1, Found: " << search(nums2, target2) << std::endl;
return 0;
}
// 函数定义
int search(const std::vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
while (left <= right) {
int 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; // 如果没有找到目标值,返回-1
}
- 输出结果:
Test 1 - Expected: 4, Found: 4
Test 2 - Expected: -1, Found: -1
- 代码仓库地址:search