旋转数组的最小数

旋转数组的最小数字

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

描述

这是一道对二分查找算法灵活运用的一道题目。
二分查找算法不限于运用在有序数组上。如果能够明确二分之后,答案存在于二分的某一侧,就可以使用二分。本题就是如此。
难度:二星
考察知识:数组,二分查找


题解

方法一:暴力方法:

直接遍历一遍数组,即可找到最小值。但是本题的附加条件就没有用上。肯定不是面试官所期望的答案。

方法二:二分查找

这种二分查找难就难在,arr[mid]跟谁比.
我们的目的是:当进行一次比较时,一定能够确定答案在mid的某一侧。一次比较为 arr[mid]跟谁比的问题。
一般的比较原则有:

  • 如果有目标值target,那么直接让arr[mid] 和 target 比较即可。
  • 如果没有目标值,一般可以考虑 端点

这里我们把target 看作是右端点,来进行分析,那就要分析以下三种情况,看是否可以达到上述的目标。

  1. 情况1,arr[mid] > target:4 5 6 1 2 3
    • arr[mid] 为 6, target为右端点 3, arr[mid] > target, 说明[first ... mid] 都是 >= target 的,因为原始数组是非递减,所以可以确定答案为 [mid+1...last]区间,所以 first = mid + 1
  2. 情况2,arr[mid] < target:5 6 1 2 3 4
    • arr[mid] 为 1, target为右端点 4, arr[mid] < target, 说明答案肯定不在[mid+1...last],但是arr[mid] 有可能是答案,所以答案在[first, mid]区间,所以last = mid;
  3. 情况3,arr[mid] == target:
    • 如果是 1 0 1 1 1, arr[mid] = target = 1, 显然答案在左边
    • 如果是 1 1 1 0 1, arr[mid] = target = 1, 显然答案在右边
      所以这种情况,不能确定答案在左边还是右边,那么就让last = last - 1;慢慢缩少区间,同时也不会错过答案。

接下来我们用个例子来说明一下:
图片说明


误区:那我们肯定在想,能不能把左端点看成target, 答案是不能。

原因:
情况1 :1 2 3 4 5 , arr[mid] = 3. target = 1, arr[mid] > target, 答案在mid 的左侧
情况2 :3 4 5 1 2 , arr[mid] = 5, target = 3, arr[mid] > target, 答案却在mid 的右侧
所以不能把左端点当做target


复杂度分析

时间复杂度:二分,所以为O(longN), 但是如果是[1, 1, 1, 1],会退化到O(n)
空间复杂度:没有开辟额外空间,为O(1)

代码

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        if (rotateArray.size() == 0) return 0;
        int first = 0, last = rotateArray.size() - 1;
        while (first < last) { // 最后剩下一个元素,即为答案
            if (rotateArray[first] < rotateArray[last]) { // 提前退出
                return rotateArray[first];
            }
            int mid = first + ((last - first) >> 1);
            if (rotateArray[mid] > rotateArray[last]) { // 情况1
                first = mid + 1;

            }
            else if (rotateArray[mid] < rotateArray[last]) { //情况2
                last = mid;
            }
            else { // 情况3
                --last;
            }
        }
        return rotateArray[first];
    }
};
全部评论
题目模棱两可。第一,都说了所有数都大于0,你官方还举例0,第二非递减排序是否为递增排序?如不是,随便一个乱的数组,你怎么找出最小的?
23 回复 分享
发布于 2020-08-06 14:23
跟左端点比较举的例子1 2 3 4 5 本身就不是旋转后的数组吧,例子都是错的
7 回复 分享
发布于 2020-08-13 20:27
非递减排序和非递减有序排序是一个概念吗?
6 回复 分享
发布于 2020-07-24 11:08
左端点可以啊 import java.util.ArrayList; public class Solution { public int minNumberInRotateArray(int [] array) { int result = 0; if(array.length == 0) { return result; } if(array.length == 1) { return array[0]; } int high = array.length; int low = 0; int middle = (high + low) >> 1; while(high >= low) { if(high - low == 1) { return array[high]; } else if(array[0] < array[middle]) { low = middle; middle = (low + high) >> 1; } else if(array[0] > array[middle]) { high = middle; middle = (low + high) >> 1; } } return 0; } }
5 回复 分享
发布于 2020-09-09 23:43
按照您给的码跑我这边是超时的;我试了几种解法,最快的是用std:sort 排序然后打出首位。。。绝了
4 回复 分享
发布于 2021-03-07 01:32
跟左端点比较也是可以的 class Solution: def minNumberInRotateArray(self, rotateArray): if not rotateArray: return 0 left = 0 right = len(rotateArray) - 1 while left < right: mid = (left + right) / 2 x = rotateArray[mid] if x == rotateArray[left]: left += 1 elif x > rotateArray[left]: left = mid else: if rotateArray[mid-1] > x: return x right = mid - 1 return rotateArray[right]
4 回复 分享
发布于 2020-08-13 15:24
[1,2,2,2,2,2]这个数组是怎么出现的?哪里旋转了?
2 回复 分享
发布于 2021-08-02 20:28
书上原话是“递增”,这里换成了“非递减”,这是两个完全不同的概念,但是解法居然是一样的。官方的回答很难让人相信正确性与权威性,建议直接看书
2 回复 分享
发布于 2021-06-10 19:13
这个解释感觉不太友好...
2 回复 分享
发布于 2020-10-12 15:37
10111怎么旋转得来的?根据题目旋转的规则无论怎么旋转都得不到这个结果吧
2 回复 分享
发布于 2020-07-09 00:11
非递减序列,没法用二分法的吧。或者说不能单纯的用二分法,等值的时候就要额外写判断逻辑了吧。
1 回复 分享
发布于 2020-12-03 16:49
第十行代码是不是有问题。应该是int mid = first + (last-first)/2;
点赞 回复 分享
发布于 2024-12-12 18:18 湖南
旋转数组问题,必须元素互不相同才能O(logn),否则就是O(n)。这题没意义,不可能做到O(n)
点赞 回复 分享
发布于 2022-02-28 11:21
arr[mid] > target, 说明[first ... mid] 都是 >= target 的,因为原始数组是非递减是什么意思
点赞 回复 分享
发布于 2022-02-12 13:39
为什么不能 左端点作为target,原因可能跟这个题目息息相关,因为题目是非递减序列求最小值,所以一定是不能左端点作为target的。如果是非递增序列求最大值,不知道是不是可以用左端点。说白了,这个题目用右端点就是针对这个题目发明的,没有为什么。。。。
点赞 回复 分享
发布于 2021-09-05 17:02
在这里说左端点作为target可以的兄弟们, 考虑一下边界条件: 1. 原非递减数组: [1, 2, 3, 4, 5, 5, 6], 前面0个元素旋转之后得的旋转数组: [1, 2, 3, 4, 5, 5, 6] 2. 原非递减数组: [1, 2, 3, 4, 5, 5, 6], 前面array.length个元素旋转之后得的旋转数组: [6, 5, 5, 4, 3, 2, 1] 如果把做端点作为target, 边界条件1就通过不了。 但是把数组最后元素作为target, 边界条件都能通过。
点赞 回复 分享
发布于 2021-09-02 11:54
二分的前提不是有序么?
点赞 回复 分享
发布于 2021-08-15 19:01
左端作为target是可行的。换个想法即可。如果target<array>array[mid],说明array[mid]是最小值,或者最小值在left-(mid-1)之间。如果两者相等,那么只能缩小left-right的范围,left++或者right--都行。 java代码: if(array.length==0) return 0; int target=array[0],l=1,r=array.length-1; while(r>=l){ int mid = l + (r-l)/2; if(target<array>array[mid]){ // 出现断层,说明最小值为array[mid],或者在左边。 target = array[mid]; r = mid - 1; }else{ // 当两者相等,收缩l-r的范围 if(target > array[l]){ target = array[l]; } l++; } } return target;</array></array>
点赞 回复 分享
发布于 2021-08-09 10:57
```java public int minNumberInRotateArray(int [] nums) { int left = 0; int right = nums.length - 1; while(left <= right){ int mid = left + (right - left) / 2; if(nums[mid] > nums[right]){ left = mid + 1; } else { right = right - 1; } } return nums[left]; } ```
点赞 回复 分享
发布于 2021-07-13 11:55
既然非递减排序,那么为啥不直接遍历,找第一个小于当前节点的值呢
点赞 回复 分享
发布于 2021-06-30 22:04

相关推荐

06-10 21:15
门头沟学院 Java
宁阿:好多这种没🧠的公司,他们估计都不知道毕业的人不能给安排实习岗
点赞 评论 收藏
分享
评论
157
19
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务