文章目录
一、题目简介
题目来源:LeetCode 官方题目描述
给定一个 升序排列数组,该数组在某个未知的轴上进行了旋转,要求找出其中的最小元素。
例如:
输入:nums = [3,4,5,1,2]
输出:1
数组原本是
[1, 2, 3, 4, 5],在元素 3 处进行了旋转,最小值为 1。
二、题意分析
这个问题的本质是:
在一个被旋转过的有序数组中快速找到最小值。
由于原数组是递增的,只是被某个点切开后拼接,所以仍保留二分查找的性质。
三、思维导图
寻找旋转排序数组中的最小值
题目理解
升序数组旋转
找最小元素
解法思路
二分查找
比较 mid 与右端点 数值关系
缩小搜索区间
特殊情况
未旋转的纯升序数组
数组中只有一个元素
复杂度分析
时间 O(log n)
空间 O(1)
Java实现
while循环
条件判断与区间更新
四、算法思路详解
思路一:线性扫描(暴力解法)
最直接的方式是遍历整个数组,找到最小值:
text
for i in nums:
minValue = min(minValue, i)
- 时间复杂度 :
O(n) - 空间复杂度 :
O(1) - 缺点:无法利用数组的有序性,效率较低。
思路二:二分查找优化解法 ------ 核心算法
由于旋转后的数组依然保持局部有序结构,我们可以通过 二分查找 实现高效查找。
核心逻辑
- 初始化左右指针:
left = 0,right = nums.length - 1 - 当
left < right时:- 计算中点
mid = (left + right) / 2 - 对比
nums[mid]与nums[right]
- 计算中点
- 根据比较结果更新搜索区间:
- 若
nums[mid] > nums[right]:最小值在右侧区间 →left = mid + 1 - 若
nums[mid] <= nums[right]:最小值在左侧区间或 mid →right = mid
- 若
- 最终
left与right相遇时,即最小值索引。
五、流程图示意
否
是
是
否
开始
初始化 left=0, right=n-1
left < right ?
返回 nums[left]
计算 mid = (left + right)/2
nums[mid] > nums[right]?
left = mid + 1
right = mid
结束
六、Java代码实现
下面是基于上述思路的 Java 示例实现:
java
public class Solution {
public int findMin(int[] nums) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] > nums[right]) {
// 最小值在右侧区间
left = mid + 1;
} else {
// 最小值在左侧区间或 mid 位置
right = mid;
}
}
// left == right 时即为最小值
return nums[left];
}
}
七、复杂度分析
| 指标 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(log n) |
使用二分查找,每次缩小一半搜索区间 |
| 空间复杂度 | O(1) |
只使用常量级变量存储指针与中间值 |
八、示例验证
输入:
nums = [4,5,6,7,0,1,2]
查找过程如下:
| 步骤 | left | right | mid | nums[mid] | nums[right] | 区间更新 |
|---|---|---|---|---|---|---|
| 1 | 0 | 6 | 3 | 7 | 2 | nums[mid] > nums[right] → left = 4 |
| 2 | 4 | 6 | 5 | 1 | 2 | nums[mid] <= nums[right] → right = 5 |
| 3 | 4 | 5 | 4 | 0 | 1 | nums[mid] <= nums[right] → right = 4 |
此时 left == right == 4,最小值为 nums[4] = 0
九、总结
| 解法 | 思路 | 时间复杂度 | 空间复杂度 |
|---|---|---|---|
| 暴力扫描 | 遍历整个数组寻找最小值 | O(n) | O(1) |
| 二分查找 | 利用有序性缩小搜索区间 | O(log n) | O(1) |
最终采取 二分查找解法 可在对数时间内高效求解。