11. 旋转数组的最小数字

剑指offer 11 旋转数组的最小数字 LeetCode154

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [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

注意:本题与主站 154 题相同:https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array-ii/

面试小提示

如果面试题要求在排序的数组(或者部分排序的数组)中查找一个数字或者统计某个数字出现的次数,那么我们的第一反应应该就是二分查找算法。而不是从头到尾遍历(太low,耗时),那样写出来了面试官也不屑的。

解法一:从头至尾遍历

为了扩充思路,这里我还是将从头至尾遍历的方法写出来了,毕竟在其他非排序的数组时,该方法还是很有用的,如股票买卖的初级问题就用到了从头至尾遍历。既然是要找到最小的数字,那就用一个变量记录最小的数字即可,不断更新这个最小值,数组遍历结束时返回该最小值。

class Solution {
    
    
    public int minArray(int[] numbers) {
    
    
        if(numbers == null || numbers.length == -1)  return 0;
        int lowest = numbers[0];//用于记录最小值
        for(int i = 1; i < numbers.length; i++){
    
    
            if(numbers[i] < lowest){
    
    
                lowest = numbers[i];//更新最小值
            }
        }
        return lowest;
    }
}

在这里插入图片描述

解法二:LeetCode官方给出的暴力解法

利用分界点左边的数肯定比右边的数小解题,如下图
在这里插入图片描述

class Solution {
    
    
    public int minArray(int[] numbers) {
    
    
        if(numbers == null || numbers.length == -1)  return 0;
        int lowest = numbers[0];//用于记录最小值
        for(int i = 1; i < numbers.length; i++){
    
    
            if(numbers[i-1] > numbers[i]){
    
    
                lowest = numbers[i];//更新最小值
                break;//已经找到最小值,可以退出循环了,小小的优化
            }
        }
        return lowest;
    }
}

在这里插入图片描述

解法三:二分查找

题目中明显透露着排序两个字,自然较优的解是涉及到二分法的。

假设我们用下图表示数组,水平线代表数字相同,横坐标代表数字下标。
在这里插入图片描述

我们发现除了最后水平的一段(黑色水平那段)之外,其余部分满足二分性质:竖直虚线左边的数满足numbers[i] ≥ numbers[0];而竖直虚线右边的数不满足这个条件

我们要找的便是不满足上诉性质的那段中的最小值。

所以我们先将最后水平的一段删除,使得右半段不满足numbers[i] ≥ numbers[0],而是严格满足numbers[i] < numbers[0]

另外,不要忘记处理数组完全单调的特殊情况:

当我们删除最后水平的一段之后,如果剩下的最后一个数大于等于第一个数,则说明数组完全单调。

时间复杂度分析
二分的时间复杂度是 O ( l o g n ) O(logn) O(logn),删除最后水平一段的时间复杂度最坏是 O ( n ) O(n) O(n),所以总时间复杂度是 O ( n ) O(n) O(n)

参考y总的题解:https://www.acwing.com/solution/content/727/

class Solution {
    
    
    public int minArray(int[] numbers) {
    
    
        if(numbers == null || numbers.length == 0) return 0;
        int left = 0;
        int right = numbers.length-1;
        //去除第二段有序数组中最后的和第一段第一个数相同的数
        //使得第二段有序数组严格满足numbers[i] < numbers[0]。
        while(right > 0 && numbers[right]==numbers[0]) right--;

        //如果此时整个数组都有序了,那么numbers[0]就是最小值
        if(numbers[0] < numbers[right]) return numbers[0];
        
        
        while(left < right){
    
    
            int mid = left + right >> 1;
            if(numbers[mid] < numbers[0]){
    
    //说明mid落在了右半段,最小值在[left,mid]里面
                right = mid;
            }else{
    
    //最小值在[mid + 1,right]之间
                left = mid + 1;
            }
        }
        return numbers[left];
    }
}

在这里插入图片描述

扩展题1:LeetCode 33 搜索旋转排序数组

本人博客题解:LeetCode 33 搜索旋转排序数组

扩展题2:LeetCode 153 寻找旋转排序数组中的最小值

本人博客题解:LeetCode 153 寻找旋转排序数组中的最小值

猜你喜欢

转载自blog.csdn.net/YouMing_Li/article/details/114266266