已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:
- 若旋转
4
次,则可以得到[4,5,6,7,0,1,2]
- 若旋转
7
次,则可以得到[0,1,2,4,5,6,7]
注意,数组 [a[0], a[1], a[2], ..., a[n-1]]
旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]]
。
给你一个元素值 互不相同 的数组 nums
,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素
。
你必须设计一个时间复杂度为 O(log n)
的算法解决此问题。
示例 1:
输入:nums = [3,4,5,1,2]
输出:1
解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。
示例 2:
输入:nums = [4,5,6,7,0,1,2]
输出:0
解释:原数组为 [0,1,2,4,5,6,7] ,旋转 3 次得到输入数组。
示例 3:
输入:nums = [11,13,15,17]
输出:11
解释:原数组为 [11,13,15,17] ,旋转 4 次得到输入数组。
提示:
n == nums.length
1 <= n <= 5000
-5000 <= nums[i] <= 5000
nums
中的所有整数 互不相同nums
原来是一个升序排序的数组,并进行了1
至n
次旋转
思路
方法1:直接返回最小值
- 首先将数组的第一个元素作为临时最小值
temp
。 - 通过一个
for
循环遍历数组中的每一个元素(从第二个元素开始)。 - 在循环中,首先检查当前元素是否小于临时最小值
temp
,如果是,则直接返回当前元素作为最小值,因为数组是有序的,而且是递增排序的,所以在遍历过程中第一个遇到的较小值即为最小值。 - 如果遍历完成后没有找到更小的值,则返回数组的第一个元素作为最小值,因为数组是有序的,所以第一个元素一定是最小值。
- 最后返回找到的最小值。
class Solution {public int findMin(int[] nums) {int temp = nums[0]; // 将数组的第一个元素作为临时最小值for (int i = 1; i < nums.length; i++) { // 遍历数组中的每一个元素(从第二个元素开始)if (nums[i] < temp) // 如果当前元素小于临时最小值return nums[i]; // 直接返回当前元素作为最小值,因为数组是有序的}return temp; // 如果遍历完成后没有找到更小的值,则返回数组的第一个元素作为最小值}
}
方法2:二分法
- 首先初始化左指针
left
为数组的起始位置,右指针right
为数组的结束位置。 - 通过一个
while
循环,当左指针小于右指针时,进行二分查找。 - 在循环中,首先计算中间位置
mid
,这里采用(left + right) / 2
的方式计算中间位置,但为了防止整型溢出,使用了(right - left) / 2 + left
的方式计算中间位置。 - 然后通过比较中间元素
nums[mid]
和数组最后一个元素nums[nums.length - 1]
的大小关系,来确定最小值的位置:- 如果中间元素大于最后一个元素,说明最小值在右半部分,因此更新左指针为
mid + 1
。 - 否则,最小值在左半部分或者就是中间元素,更新右指针为
mid
。
- 如果中间元素大于最后一个元素,说明最小值在右半部分,因此更新左指针为
- 最终返回左指针
left
对应的值,即搜索到的满足条件的最小值。
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[nums.length - 1]) { // 如果中间元素大于最后一个元素,说明最小值在右半部分left = mid + 1; // 更新左指针为mid的下一个位置} else {right = mid; // 否则,最小值在左半部分或者就是mid处,更新右指针为mid}}return nums[left]; // 返回左指针对应的值,即搜索到的满足条件的最小值}
}