把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
给你一个可能存在 重复 元素值的数组 numbers ,它原来是一个升序排列的数组,并按上述情形进行了一次旋转。请返回旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一次旋转,该数组的最小值为1。
示例 1:
输入:[3,4,5,1,2]
输出:1
示例 2:
输入:[2,2,2,0,1]
输出:0
分析:
方法1:普通遍历优化
最简单的解法就是直接遍历记录每一次的最小值,不过考虑到数组是一个旋转数组,那么可以优化遍历条件,因为大部分数组的是一个两段升序的情况,而且第二段升序的第一个数字就是数组最小值,所以当遍历到有值比第一个值小时,直接返回该值,终止循环。
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public int minArray(int[] numbers) {
int len = numbers.length;
for(int i = 1; i < len; ++i){
if(numbers[0] > numbers[i]){
return numbers[i];
}
}
return numbers[0];
}
}
方法2:二分查找
由方法1知道大部分数组第二段升序的第一个数字就是数组最小值,拿 [3,4,5,1,2] 举例,最小值就是 1,那么我们是不是就可以从中间开始查找,定义两个指针 i ,j 分别指向开头和结尾,令 m = (i + j) / 2 为中间点,指针满足三个条件:
-
numbers[ j ] < numbers[ m ] 时,最小值一定在中间点右边,即在区间 ( m , j ] 内,因此令 i = m + 1。
-
numbers[ j ] > numbers[ m ] 时,因为 m 是整除,所以最小值一定在中间点左边或中间点,即在区间 [ i , m ] 内,因此令 j = m。
-
numbers[ j ] = numbers[ m ] 时,不确定在那边,不过因为是两个一样的数,所以我们可以舍弃其中的一个,但必须保证最小值在新的区间内。因为当两个点值相等的时候,[ i , m ] 一定满足升序条件,所以我们只能令 j = j - 1。
时间复杂度:O(log2 n)
空间复杂度:O(1)
class Solution {
public int minArray(int[] numbers) {
int i = 0, j = numbers.length - 1;
while(i < j){
int m = (i + j) / 2;
if(numbers[j] < numbers[m]){
i = m + 1;
continue;
}
if(numbers[j] > numbers[m]){
j = m;
continue;
}
j--;
}
return numbers[i];
}
}
题目来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-lcof