题解 | #旋转数组的最小数字#

旋转数组的最小数字

http://www.nowcoder.com/practice/9f3231a991af4f55b95579b44b7a01ba

题目的主要信息:
  • 有一个长度为 n 的非降序数组,把一个数组最开始的若干个元素“平移”到数组的末尾,变成一个旋转数组
  • 找到这个旋转数组的最小值
举一反三:

学习完本题的思路你可以解决如下题目:

BM17.二分查找-I

BM18.二维数组中的查找

BM19.寻找峰值

方法一:二分法(推荐使用)

知识点:分治

分治即“分而治之”,“分”指的是将一个大而复杂的问题划分成多个性质相同但是规模更小的子问题,子问题继续按照这样划分,直到问题可以被轻易解决;“治”指的是将子问题单独进行处理。经过分治后的子问题,需要将解进行合并才能得到原问题的解,因此整个分治过程经常用递归来实现。

思路:

旋转数组将原本有序的数组分成了两部分有序的数组,因为在原始有序数组中,最小的元素一定是在首位,旋转后无序的点就是最小的数字。我们可以将旋转前的前半段命名为A,旋转后的前半段命名为B,旋转数组即将AB变成了BA,我们想知道最小的元素到底在哪里。

因为A部分和B部分都是各自有序的,所以我们还是想用分治来试试,每次比较中间值,确认目标值(最小元素)所在的区间。

具体做法:

  • step 1:双指针指向旋转后数组的首尾,作为区间端点。
  • step 2:若是区间中点值大于区间右界值,则最小的数字一定在中点右边。
  • step 3:若是区间中点值等于区间右界值,则是不容易分辨最小数字在哪半个区间,比如[1,1,1,0,1],应该逐个缩减右界。
  • step 4:若是区间中点值小于区间右界值,则最小的数字一定在中点左边。
  • step 5:通过调整区间最后即可锁定最小值所在。

图示:

alt

Java实现代码:

import java.util.ArrayList;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        int left = 0;
        int right = array.length - 1;
        while(left < right){
            int mid = (left + right) / 2;
            //最小的数字在mid右边
            if(array[mid] > array[right]) 
                left = mid + 1;
            //无法判断,一个一个试
            else if(array[mid] == array[right]) 
                right--;
            //最小数字要么是mid要么在mid左边
            else 
                right = mid;
        }
        return array[left];
    }
}

C++实现代码:

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        int left = 0;
        int right = rotateArray.size() - 1;
        while(left < right){
            int mid = (left + right) / 2;
            //最小的数字在mid右边
            if(rotateArray[mid] > rotateArray[right]) 
                left = mid + 1;
            //无法判断,一个一个试
            else if(rotateArray[mid] == rotateArray[right]) 
                right--;
            //最小数字要么是mid要么在mid左边
            else 
                right = mid;
        }
        return rotateArray[left];
    }
};

Python实现代码

class Solution:
    def minNumberInRotateArray(self , rotateArray: List[int]) -> int:
        left = 0
        right = len(rotateArray) - 1
        while left < right:
            mid = int((left + right) / 2) 
            #最小的数字在mid右边
            if rotateArray[mid] > rotateArray[right]: 
                left = mid + 1 
            #无法判断,一个一个试
            elif rotateArray[mid] == rotateArray[right]: 
                right -= 1
            #最小数字要么是mid要么在mid左边
            else:
                right = mid 
        return rotateArray[left]

复杂度分析:

  • 时间复杂度:O(log2n)O(log_2n),二分法最坏情况对nn取2的对数
  • 空间复杂度:O(1)O(1),常数级变量,无额外辅助空间
方法二:遍历查找(扩展思路)

思路:

再怎么旋转,这也是一个数组,我们要找的数组最小值。更通用的解法莫过于遍历数组,维护最小值。

具体做法:

  • step 1:因为数组长度不小于1,因此最初的最小值可以设置为数组首元素或者int型最大值。
  • step 2:从左到右遍历整个数组,依次检查当前元素与记录元素的大小,若是当前元素更小,则记录最小的元素更新。
  • step 3:遍历结束,最终记录的最小值即是所求。

Java实现代码:

import java.util.*;
public class Solution {
    public int minNumberInRotateArray(int [] array) {
        //数组一定有元素
        int res = array[0]; 
        //遍历数组
        for(int i = 1; i < array.length; i++) 
            //每次维护最小值
            res = Math.min(res, array[i]); 
        return res;
    }
}

C++实现代码:

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        int res = INT_MAX;
        //遍历数组
        for(int i = 0; i < rotateArray.size(); i++) 
            //每次维护最小值
            res = min(res, rotateArray[i]); 
        return res;
    }
};

Python实现代码

class Solution:
    def minNumberInRotateArray(self , rotateArray: List[int]) -> int:
        # 数组一定有元素
        res = rotateArray[0] 
        # 遍历数组
        for i in range(len(rotateArray)): 
            # 每次维护最小值
            res = min([res, rotateArray[i]]) 
        return res

复杂度分析:

  • 时间复杂度:O(n)O(n),其中nn为数组大小,遍历一次数组
  • 空间复杂度:O(1)O(1),常数级变量,无额外辅助空间
全部评论
方法一:二分法 的时间复杂度有问题,如果数组中所有元素都相等,则需要执行循环o(n)次,一旦数组有重复这道题没有o(logn)的解法,
2 回复 分享
发布于 2022-10-23 00:40 北京
方法一的二分法不对
2 回复 分享
发布于 2022-11-03 12:17 山东
二分法那里,“若是区间中点值小于区间右界值,则最小的数字一定在中点左边。”既然是这样,为什么if(nums[mid] < nums[right])时,不能写right = mid - 1;为什么最小数在区间[left,mid]里,不是区间[left,mid -1]?我要是写right = mid - 1;不通过,给了一个错误的例子[1,0,1,1,1],但是这个例子并不符合题目的数组要求啊,题目说给一个非递减的数组进行旋转,还说旋转是把数组前若干个元素放到数组末尾,那这个例子怎么会是这样?1后面是0?
点赞 回复 分享
发布于 01-04 17:14 黑龙江
二分法那里每个if里要加continue或者每次重新计算mid的值,不然left或者right改变会影响mid的值导致 right--;意外执行,在[1,0,1,1,1]这个例子里输出错误的答案
1 回复 分享
发布于 01-10 18:16 台湾
求最小值,不是最小值的下表,直接对序列排序,获取第一个元素的值不行吗?nums.sort(),return nums[0]
点赞 回复 分享
发布于 03-06 10:55 北京

相关推荐

10-25 12:05
已编辑
湖南科技大学 Java
若梦难了:我有你这简历,已经大厂乱杀了
点赞 评论 收藏
分享
评论
31
7
分享
牛客网
牛客企业服务