📌 题目解析
题目:给定一个旋转排序数组 nums,其中无重复元素,要求找到数组中的最小值。
旋转排序数组的特点
• 初始为递增排序数组,某个位置 k 进行了旋转。
• 旋转后,前 k 个元素移到了数组的后面,导致前大后小的变化。
• 例如:
原数组: [1, 2, 3, 4, 5, 6, 7]
旋转后: [4, 5, 6, 7, 1, 2, 3] (最小值 1)
• 数组可以分成两个单调递增部分:
• 左半部分:nums[left] >= nums[0]
• 右半部分:nums[right] <= nums[right-1]
🚀 二分查找思路
目标:利用 二分查找 在 O(log n) 时间内找到最小值
核心逻辑:
1. 检查数组是否已排序
• 如果 nums[left] < nums[right],说明没有旋转,直接返回 nums[left]。
2. 二分查找
• 计算 mid = left + (right - left) / 2
• 对 nums[mid] 和 nums[right] 进行比较:
• 若 nums[mid] > nums[right]
• mid 处于左半部分,最小值一定在右侧
• 移动 left = mid + 1
• 若 nums[mid] ≤ nums[right]
• mid 可能是最小值,或最小值在 mid 左侧
• 移动 right = mid(保留 mid)
• 循环直到 left == right,返回 nums[left]
✅ 代码实现
class Solution {
public:
int findMin(vector<int>& nums) {
int left = 0, right = nums.size() - 1;
// 如果数组是有序的(未旋转)
if (nums[left] < nums[right]) return nums[left];
while (left < right) { // 终止条件:left == right
int mid = left + (right - left) / 2;
if (nums[mid] > nums[right]) {
// mid 在左侧,最小值一定在右半部分
left = mid + 1;
} else {
// mid 在右侧,最小值可能在 mid 及其左侧
right = mid;
}
}
return nums[left]; // 结束时 left == right,返回最小值
}
};
📝 运行步骤
我们用 nums = [4, 5, 6, 7, 1, 2, 3] 作为示例,跟踪每一步的 left、right、mid 变化。
迭代 | left | right | mid | nums[mid] | nums[right] | 操作 |
---|---|---|---|---|---|---|
初始化 | 0 | 6 | 3 | 7 | 3 | nums[left] > nums[right],进入二分查找 |
1 | 0 | 6 | 3 | 7 | 3 | nums[mid] > nums[right] → left = mid + 1 = 4 |
2 | 4 | 6 | 5 | 2 | 3 | nums[mid] ≤ nums[right] → right = mid = 5 |
3 | 4 | 5 | 4 | 1 | 2 | nums[mid] ≤ nums[right] → right = mid = 4 |
结束 | left == right == 4 | - | - | - | - | 返回 nums[4] = 1 |
最终答案:1
📊 复杂度分析
时间复杂度 | 空间复杂度 |
---|---|
O(log n) | O(1) |
• 时间复杂度:每次二分查找缩小一半范围,O(log n)
• 空间复杂度:只使用了 left 和 right 变量,O(1)
🔍 总结
• 使用二分查找,而不是遍历整个数组 O(n)。
• 核心判断条件:
1. nums[mid] > nums[right],最小值在右侧
2. nums[mid] ≤ nums[right],最小值在左侧或 mid
• 终止条件:left == right,返回 nums[left] 即最小值。
这样,我们就能高效地找到旋转排序数组中的最小值 🚀