843 字
4 分钟
搜索旋转排序数组
题目描述
整数数组 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) 的算法解决此问题。
解题思路
- 二分查找框架:
- 初始化左右指针
left = 0,right = nums.length - 1 - 循环条件
while (left <= right)
- 初始化左右指针
- 判断区间有序性:
- 计算中点
mid = (left + right) >> 1 - 若
nums[left] <= nums[mid]→ 左区间有序 - 否则 → 右区间有序
- 计算中点
- 目标值检测:
- 左区间有序时:
- 若
target ∈ [nums[left], nums[mid])→ 右移右指针right = mid - 1 - 否则 → 左移左指针
left = mid + 1
- 若
- 右区间有序时:
- 若
target ∈ (nums[mid], nums[right]]→ 左移左指针left = mid + 1 - 否则 → 右移右指针
right = mid - 1
- 若
- 左区间有序时:
- 终止条件:
nums[mid] === target时立即返回索引- 循环结束未找到则返回
-1
关键洞察
- 局部有序性:
- 旋转数组 必有一侧区间保持有序
- 通过
nums[left] <= nums[mid]快速判定有序区间
- 区间选择逻辑:
- 目标值在有序区间内 → 继续搜索该区间
- 目标值在无序区间 → 转向另一侧区间搜索
- 本质是 利用有序性缩小搜索范围
- 边界精确控制:
- 比较时包含等号(如
<=,>=)防止遗漏边界值 - 区间端点值必须参与判断(旋转点可能在任意位置)
- 比较时包含等号(如
- 对数复杂度保证:
- 每次迭代排除一半区间 → O(log n) 时间复杂度
- 空间复杂度 O(1)(仅需指针变量)
复杂度分析
| 类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(log n) | 二分查找每次排除一半区间 |
| 空间复杂度 | O(1) | 仅使用常量级额外空间 |
代码实现
var search = function (nums, target) { let left = 0, right = nums.length - 1; while (left <= right) { const mid = left + Math.floor((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; // 未找到目标值};实际应用场景
- 日志时间检索:旋转的日志时间戳中查找特定记录`
- 磁盘数据恢复:恢复被旋转的数据库索引`
- 传感器数据分析:周期性旋转数据中定位异常值`
- 密码破解:旋转加密数据中寻找特征值`