首页 > 试题广场 >

旋转数组的最小数字

[编程题]旋转数组的最小数字
  • 热度指数:1413083 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。

数据范围:,数组中任意元素的值: 
要求:空间复杂度: ,时间复杂度:
示例1

输入

[3,4,5,1,2]

输出

1
示例2

输入

[3,100,200,3]

输出

3
推荐
思路

剑指Offer中有这道题目的分析。这是一道二分查找的变形的题目。


旋转之后的数组实际上可以划分成两个有序的子数组:前面子数组的大小都大于后面子数组中的元素

注意到实际上最小的元素就是两个子数组的分界线。本题目给出的数组一定程度上是排序的,因此我们试着用二分查找法寻找这个最小的元素。

思路:

(1)我们用两个指针left,right分别指向数组的第一个元素和最后一个元素。按照题目的旋转的规则,第一个元素应该是大于最后一个元素的(没有重复的元素)。

但是如果不是旋转,第一个元素肯定小于最后一个元素。

(2)找到数组的中间元素。

中间元素大于第一个元素,则中间元素位于前面的递增子数组,此时最小元素位于中间元素的后面。我们可以让第一个指针left指向中间元素。

移动之后,第一个指针仍然位于前面的递增数组中。

中间元素小于第一个元素,则中间元素位于后面的递增子数组,此时最小元素位于中间元素的前面。我们可以让第二个指针right指向中间元素。

移动之后,第个指针仍然位于面的递增数组中。

这样可以缩小寻找的范围。

(3)按照以上思路,第一个指针left总是指向前面递增数组的元素,第二个指针right总是指向后面递增的数组元素。

最终第一个指针将指向前面数组的最后一个元素,第二个指针指向后面数组中的第一个元素。

也就是说他们将指向两个相邻的元素,而第二个指针指向的刚好是最小的元素,这就是循环的结束条件。

到目前为止以上思路很耗的解决了没有重复数字的情况,这一道题目添加上了这一要求,有了重复数字。

因此这一道题目比上一道题目多了些特殊情况:

我们看一组例子:{1,0,11,1} 和 {1,1, 1,0,1} 都可以看成是递增排序数组{0,1,1,1,1}的旋转。

这种情况下我们无法继续用上一道题目的解法,去解决这道题目。因为在这两个数组中,第一个数字,最后一个数字,中间数字都是1。

第一种情况下,中间数字位于后面的子数组,第二种情况,中间数字位于前面的子数组。

因此当两个指针指向的数字和中间数字相同的时候,我们无法确定中间数字1是属于前面的子数组(绿色表示)还是属于后面的子数组(紫色表示)。

也就无法移动指针来缩小查找的范围。

代码
#include <iostream>
#include <vector>
#include <string>
#include <stack>
#include <algorithm>
using namespace std;

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        int size = rotateArray.size();
        if(size == 0){
            return 0;
        }//if
        int left = 0,right = size - 1;
        int mid = 0;
        // rotateArray[left] >= rotateArray[right] 确保旋转
        while(rotateArray[left] >= rotateArray[right]){
            // 分界点
            if(right - left == 1){
                mid = right;
                break;
            }//if
            mid = left + (right - left) / 2;
            // rotateArray[left] rotateArray[right] rotateArray[mid]三者相等
            // 无法确定中间元素是属于前面还是后面的递增子数组
            // 只能顺序查找
            if(rotateArray[left] == rotateArray[right] && rotateArray[left] == rotateArray[mid]){
                return MinOrder(rotateArray,left,right);
            }//if
            // 中间元素位于前面的递增子数组
            // 此时最小元素位于中间元素的后面
            if(rotateArray[mid] >= rotateArray[left]){
                left = mid;
            }//if
            // 中间元素位于后面的递增子数组
            // 此时最小元素位于中间元素的前面
            else{
                right = mid;
            }//else
        }//while
        return rotateArray[mid];
    }
private:
    // 顺序寻找最小值
    int MinOrder(vector<int> &num,int left,int right){
        int result = num[left];
        for(int i = left + 1;i < right;++i){
            if(num[i] < result){
                result = num[i];
            }//if
        }//for
        return result;
    }
};

int main(){
    Solution s;
    //vector<int> num = {0,1,2,3,4,5};
    //vector<int> num = {4,5,6,7,1,2,3};
    vector<int> num = {2,2,2,2,1,2};
    int result = s.minNumberInRotateArray(num);
    // 输出
    cout<<result<<endl;
    return 0;
}
编辑于 2015-08-18 23:34:39 回复(141)
贾克斯:看了一遍讨论区的代码,一个能打的都没有。
import java.util.*;

public class Solution {
    public int minNumberInRotateArray (int[] nums) {
        Integer min = Integer.MIN_VALUE;
        int start = 0;
        int end = nums.length - 1;
        return min(nums,start,end);
    }
    /**
        递归+二分法实现,简练

     */
    public int min(int[] nums,int start ,int end){
        while(start < end){
            int mid = (start + end)/2;
            if(nums[mid] > nums[end]){
                // 中间大于尾部时 比如2,1
                start = mid + 1;
            }else if(nums[mid] < nums[end]){
                //尾部大于中间时。比如1,2
                end = mid;
            }else{
                // 中间等于尾部时,比如1,2,2或者2,1,1
                Integer minValue = min(nums,start,mid);
                Integer minValue2 = min(nums,mid + 1,end);
                return Math.min(minValue,minValue2);
            }
        }
        return nums[start]; 
    }
}


发表于 2024-08-24 22:08:51 回复(0)
public int minNumberInRotateArray (int[] nums) {
        int m = minNumberInRotateArray2(nums);
        return nums[m];
    }

    public int minNumberInRotateArray2 (int[] nums) {
        int s = 0,e = nums.length-1;
        int m = 0;
        while(s < e){
             m = (s+e) / 2;
            if(nums[m] > nums[e]){
                s = m+1;
            }else if(nums[m] < nums[e]){
                e = m;
            }else{
                e--;
            }
        }
        return s;
    }

    public int minNumberInRotateArray1 (int[] nums) {
        int m = minNumberInRotateArray1_1(nums,0,nums.length-1);
        return nums[m];
    }

    public int minNumberInRotateArray1_1 (int[] nums,int s,int e) {
        if(s >= e) return s;
        int m = (s+e)/2;
        int n1 = minNumberInRotateArray1_1(nums,s,m);
        int n2 = minNumberInRotateArray1_1(nums,m+1,e);
        if(nums[n1] < nums[n2])
            return n1;
        else
            return n2;
    }

发表于 2024-08-22 17:56:46 回复(0)
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param nums int整型一维数组
     * @return int整型
     */
    public int minNumberInRotateArray (int[] nums) {
        // write code here
        // 整个问题本是上是在找数组中唯一存在的极小值,采取二分法解决

        // 算法的时间复杂度O(logN),额外空间复杂度O(1)

        int n = nums.length;
        if (n == 1) {
            // 如果只有一个元素
            return nums[0];
        }
        if (n == 2) {
            // 如果只有两个元素
            return Math.min(nums[0], nums[1]);
        }
        // 确保有三个以上的元素
        int l = 0;
        int r = n - 1;

        // 采用二分思想,任务是找到这样一个转折点[↗……↗(↘index)↗……↗],其特征是左边比它大,右边也比它大
        int min = nums[l];
        while (l < r) {
            // 因为数组非递减,且涉及到右划l
            // 所以采用一个变量min时刻与当前最左的l元素比较总不是坏事
            min = Math.min(min, nums[l]);
            int mid = (l + r) / 2;
            if (nums[l] > nums[mid]) {
                // 如果mid处比最左处要小,即[↗……↗(↘index)↗……mid……↗],说明局部最小值一定在左半区(或mid本身)
                r = mid;
            } else if (nums[l] < nums[mid]) {
                // 如果mid处比最左处要大,即[↗……mid……↗(↘index)↗……↗],说明局部最小值一定在右半区(必不包括mid)
                l = mid + 1;
            } else {
                // 如果mid处与最左处相等,即[-->……mid……-->(↘index)↗……-->],或[-->……-->(↘index)↗……-->……mid……-->],在左还是在右不能确定
                // 但是!至少说明l处一定不是极小值点,此时l往右移一位,缩小范围即可
                l++;
            }
        }

        return Math.min(min, nums[l]);
    }
}

发表于 2024-07-27 22:18:11 回复(0)
import java.util.*;

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param nums int整型一维数组
     * @return int整型
     */
    public int minNumberInRotateArray (int[] nums) {
        // write code here
        //定义最小下标
        int min = 0;
        for(int i=0;i<nums.length-1;i++){
            for(int j=i+1;j<nums.length;j++){
                if(nums[j]<nums[min]){
                    min = j;
                }
            }
        }
        return nums[min];
    }
}
发表于 2024-07-16 13:31:07 回复(0)
import java.util.*;
class Solution {
    public int minNumberInRotateArray (int[] nums) {
        int l = 0;
        int r = nums.length;
        while (l < r) {
            int mid = (l + r) / 2;
            if (nums[mid] > nums[r - 1]) {
                l = mid + 1;
            } else if (nums[mid] == nums[r - 1]) {
                r--;
            } else {
                r = mid;
            }
        }
        return nums[l];
    }
}
大佬帮忙看看为什么右边取开区间不行, 取闭区间就能过

发表于 2024-04-21 19:06:40 回复(0)
使用Java暴力破解:
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param nums int整型一维数组
     * @return int整型
     */
    public int minNumberInRotateArray (int[] nums) {
        // write code here
        if (nums.length == 0)
            return 0;
        //假设第一个元素是最小的
        int min = nums[0];
        for (int i = 1; i < nums.length; i++) {
            if (nums[i] < min)
                // 如果找到更小的元素,则更新最小值
                min = nums[i];
        }
        return min;
    }
}


编辑于 2024-04-11 10:53:53 回复(0)
import java.util.*;
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param nums int整型一维数组
     * @return int整型
     */
    public int minNumberInRotateArray (int[] nums) {
        // if(nums.length==1||nums[0]==nums[nums.length-1]){
        //     return nums[0];
        // }
        int l = 0;
        int r = nums.length - 1;
        while (l <= r) {
            int mid = (l + r) / 2;
            if (l == r) {
                return nums[r];
            }
            if (nums[mid] > nums[r])
                l = mid + 1;
            else if (nums[mid] == nums[r]) {
                if ((mid + 1 <= nums.length - 1) && nums[mid + 1] != nums[mid]) {
                    l = mid + 1;
                } else {
                    r = mid;
                }
            } else {
                r = mid;
            }
        }
        //走不到这,写着为了不标红,看着心烦
        return -1;
    }
}
编辑于 2024-04-06 20:54:09 回复(1)
import java.util.*;

public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param nums int整型一维数组
* @return int整型
*/
public static int minNumberInRotateArray (int[] nums) {
int l=nums.length;
if (nums[0]<nums[l-1]){
return nums[0];
}
int s=0;
int e=l-1;
while (nums[0]==nums[e]){
e--;
}
while (s<e){
int mid=(s+e)/2;
if (nums[mid]>nums[e]){
s=mid+1;
}else {
e=mid;
}
}
return nums[s];
}
}
编辑于 2024-04-04 17:52:59 回复(1)
有没有大佬看出其中的错误,总感觉不是很对
public int minNumberInRotateArray (int[] nums) {
        // write code here
        int c=1;
        if(nums.length==0){
            return 0;
        }
        for(int i=0;i<nums.length-1;i++){
            if(nums[i]>nums[i+1]){
                c= nums[0]>nums[i+1]?nums[i+1]:nums[0];
            }
        }
        return c>nums[nums.length-1]?nums[nums.length-1]:c;
    }
发表于 2024-03-27 21:04:54 回复(1)
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param nums int整型一维数组 
     * @return int整型
     */
    public int minNumberInRotateArray (int[] nums) {
        // 这里可以将数组分解为两部分
        // 这两部分均为 从小到大排序的数组,以 12345 为例
        // 会有 34512、45123 等

        // write code here
        // 先来判断一下数组为空的情况
        if(nums.length == 0) {
            return 0;
        }

        // 使用二分法来解决
        int start = 0;
        int end = nums.length - 1;
        // 这里是旋转数组,先来考虑第一种情况
        // 当这里的第一个元素的值 比 最后一个元素的值小
        // 对于上面的例子,可以断定这里的第一个值 就一定是 最小值
        if(nums[start] < nums[end]) {
            return nums[start];
        }

        // 之后通过 二分法来判断最小值
        // 先来说明没有重复值的情况
        // 不断的通过 左、右 侧加逼 
        // 最终会使得 start 在左侧部分的最大值(最后一个位置) 上
        // 并且会使得 end 在右侧部分上达到其中的 最小值(这个就是我们需要的值)
        // 此时 start 和 end 就是一个相邻的关系,所以对于 while 设定这样一个判断条件
        while(start != end-1) {
            int mid = (start + end) / 2;
            // 在这之中还需要处理多个重复数据的情况,并且处理三个指针重叠后的情况
            // 如题例:1,0,1,1,1
            if(nums[start] == nums[end] && nums[start] == nums[mid]) {
                // 这里定义一个方法专门来处理这个问题
                return minNum(nums,start,end);
            }
            if(nums[mid] >= nums[start]) {
                start = mid;
            }else if(nums[mid] <= nums[end]){
                end = mid;
            }
        }
        // 在完成上面的判断后,这里的 end 位置的值就是我们要找到的最小值
        return nums[end];
    }

    private int minNum(int[] nums, int start, int end) {
        // 这里需要注意的是,要将值设置为 当前重合处的值,不能设置为 0 !!!
        int re = nums[start];
        for (int i=0; i < nums.length; i++) {
            if(re > nums[i]) {
                // 将这里的最小值记录下来并返回
                re = nums[i];
            }
        }
        return re;
    }
}

发表于 2024-03-23 11:00:30 回复(0)
public int minNumberInRotateArray (int[] nums) {
        // write code here
        if(nums.length <= 0){
            return 0;
        }
        int rt = nums[0];
        for(int info : nums){
            if(rt > info){
                rt = info;
            }
        }
        return rt;
    }

不考虑二分是不是更快?

发表于 2024-03-14 02:15:43 回复(0)
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param nums int整型一维数组
     * @return int整型
     */
    public int minNumberInRotateArray (int[] nums) {
        // write code here
        return binary(nums, 0, nums.length - 1);
    }

    public int binary(int[] nums, int left, int right) {
        if (left >= right || nums[left] < nums[right]) {
            return nums[left];
        }
        int mid = left + (right - left) / 2;
        int leftNum = binary(nums, left, mid);
        int rightNum = binary(nums, mid + 1, right);
        return leftNum <= rightNum ? leftNum : rightNum;
    }

}


发表于 2023-12-13 14:19:10 回复(0)
import java.util.*;

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param nums int整型一维数组
     * @return int整型
     */
    public int minNumberInRotateArray (int[] nums) {
        // write code here
        if(nums.length==0){
            return 0;
        }
        int ans = nums[0];
            for(int i=0;i<nums.length;i++){
                ans=Math.min(ans,nums[i]);
             
        }
        return ans;
    }
}
发表于 2023-10-29 18:37:23 回复(0)
 
public int minNumberInRotateArray (int[] nums) {
        // write code here
        int min=nums[0];
        for(int i=1;i<nums.length;i++){
            if(min>nums[i]){
                min=nums[i];
            }
        }
        return min;
    }
发表于 2023-10-26 15:38:12 回复(2)
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param nums int整型一维数组 
     * @return int整型
     */
    public int minNumberInRotateArray (int[] nums) {
        // write code here
        a(nums,0,nums.length-1);
        return nums[0];
    }
    public void a(int[] nums,int left,int right){
        if(left>=right)
            return;
        int mid=(left+right)/2;
        a(nums,left,mid);
        a(nums,mid+1,right);
        b(nums,left,mid,right);
    }
     public void b(int[] nums,int left,int mid,int right){
        if(nums[left]>nums[mid+1]){
            nums[left]=nums[mid+1];
        }
    }
}

发表于 2023-10-09 16:45:37 回复(0)
啥旋转啊,没看懂,不是这样就好了吗,直接过了啊
        Arrays.sort(nums);
        return nums[0];


发表于 2023-09-18 11:16:38 回复(2)
    public int minNumberInRotateArray (int[] nums) {
        int minVal = findMinVal(nums, 0, nums.length - 1); // 分治算法
        return minVal;
    }
    private int findMinVal(int[] nums, int l, int r) {
        if (l == r) {
            return nums[l];
        }
        if (nums[l] < nums[r]) {
            return nums[l];
        }
        int c = (l + r) / 2;
        int minVal01 = findMinVal(nums, l, c);
        int minVal02 = findMinVal(nums, c + 1, r);
        return minVal01 < minVal02 ? minVal01 : minVal02;
    }
发表于 2023-09-16 21:52:28 回复(0)
    public int minNumberInRotateArray (int[] nums) {
        if (nums.length == 0) {
            return 0;
        }
        for (int i = 0; i < nums.length; i++) {
            if (i == nums.length - 1) {
                return nums[0];
            }
            if (nums[i + 1] < nums[i]) {
                return nums[i + 1];
            }
        }
        return 0;
    }
以上代码亦可通过测试,是否可以视为一种解题方式

发表于 2023-09-13 22:29:51 回复(0)

问题信息

难度:
423条回答 279941浏览

热门推荐

通过挑战的用户

查看代码
旋转数组的最小数字