目录
[一、LeetCode 33:搜索旋转排序数组](#一、LeetCode 33:搜索旋转排序数组)
[核心思路:二分 + 有序区间判断](#核心思路:二分 + 有序区间判断)
[Java 完整实现](#Java 完整实现)
[二、LeetCode 153:寻找旋转排序数组中的最小值](#二、LeetCode 153:寻找旋转排序数组中的最小值)
[核心思路:二分 + 无序区间定位](#核心思路:二分 + 无序区间定位)
[Java 完整实现](#Java 完整实现)
二刷二分查找,必须拿下旋转排序数组的两道高频变形题。这两道题都是普通二分的进阶应用,核心是利用「旋转后数组必有一半有序」的特性,在对数级时间内完成搜索,也是面试中二分变形的必考题。
一、LeetCode 33:搜索旋转排序数组
题目描述
整数数组 nums 按升序排列,在某个点上进行了旋转(例如 [0,1,2,4,5,6,7] 变为 [4,5,6,7,0,1,2])。在数组中搜索目标值 target,如果存在返回索引,否则返回 -1。要求算法时间复杂度为 O (log n)。
核心思路:二分 + 有序区间判断
普通二分无法直接用于旋转数组,关键是每次二分后,总有一半区间是有序的:
- 计算
mid,判断左半区间[left, mid]是否有序(nums[left] <= nums[mid]); - 若左半有序,且
target在[nums[left], nums[mid]]范围内,则收缩右边界到mid-1;否则目标一定在右半区间,收缩左边界到mid+1; - 若右半区间
[mid, right]有序(nums[mid] <= nums[right]),且target在[nums[mid], nums[right]]范围内,则收缩左边界到mid+1;否则目标一定在左半区间,收缩右边界到mid-1; - 循环结束仍未找到,返回
-1。
Java 完整实现
java
运行
class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
return mid;
}
// 判断左半区间是否有序
if (nums[left] <= nums[mid]) {
// target在左半有序区间内,收缩右边界
if (target >= nums[left] && target < nums[mid]) {
right = mid - 1;
} else {
// 否则目标在右半无序区间
left = mid + 1;
}
} else {
// 右半区间有序
if (target > nums[mid] && target <= nums[right]) {
// target在右半有序区间内,收缩左边界
left = mid + 1;
} else {
// 否则目标在左半无序区间
right = mid - 1;
}
}
}
return -1;
}
}
复杂度分析
- 时间复杂度:O (log n),标准二分查找的对数级复杂度;
- 空间复杂度:O (1),原地操作,无额外空间开销。
二、LeetCode 153:寻找旋转排序数组中的最小值
题目描述
已知一个长度为 n 的数组,预先按照升序排列,经过了多次旋转(每次旋转将最右侧元素移到最左侧)。例如原数组 [0,1,2,4,5,6,7] 旋转后可能为 [4,5,6,7,0,1,2]。请找出数组中的最小值,数组无重复元素,要求时间复杂度为 O (log n)。
核心思路:二分 + 无序区间定位
和 33 题类似,核心还是利用「必有一半有序」的特性:
- 若
nums[mid] > nums[right],说明最小值一定在右半无序区间,将左边界移动到mid + 1; - 若
nums[mid] < nums[right],说明右半区间有序,最小值一定在左半区间(包括mid),将右边界移动到mid; - 循环结束时
left == right,即为最小值的索引。
Java 完整实现
java
运行
class Solution {
public int findMin(int[] nums) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = left + (right - left) / 2;
// mid > right 说明最小值在右半无序区间
if (nums[mid] > nums[right]) {
left = mid + 1;
} else {
// 右半有序,最小值在左半(含mid)
right = mid;
}
}
return nums[left];
}
}
复杂度分析
- 时间复杂度:O (log n),标准二分查找的对数级复杂度;
- 空间复杂度:O (1),原地操作,无额外空间开销。
三、两道题核心对比
表格
| 对比项 | LeetCode 33 搜索目标值 | LeetCode 153 找最小值 |
|---|---|---|
| 核心考点 | 有序区间判断 + 目标值定位 | 无序区间定位 + 最小值边界 |
| 关键逻辑 | 每次判断哪一半有序,再判断目标是否在有序区间 | 每次通过 nums[mid] 和 nums[right] 比较,定位最小值所在区间 |
| 循环条件 | left <= right(普通二分模板) |
left < right(边界逼近模板) |
| 面试高频问题 | 如何处理数组无旋转的情况?如何判断有序区间? | 为什么用 nums[mid] 和 nums[right] 比较,不用 nums[left]? |
四、二刷复盘感悟
- 二分变形的核心是「有序区间的判断」:不管数组如何旋转,只要是升序旋转数组,每次二分后必然有一半区间是有序的,这是解题的关键突破口;
- 边界模板要区分使用场景 :33 题用
left <= right的普通二分模板,153 题用left < right的边界逼近模板,不同场景下选择合适的模板能减少边界错误; - 拓展延伸 :如果数组存在重复元素(如 LeetCode 81 题),判断有序区间时需要额外处理
nums[left] == nums[mid] == nums[right]的情况,此时只能left++收缩边界,最坏时间复杂度会退化为 O (n)。