旋转数组的最小数

旋转数组的最小数字

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
跟左端点比较也是可以的 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
按照您给的码跑我这边是超时的;我试了几种解法,最快的是用std:sort 排序然后打出首位。。。绝了
4
送花
回复
分享
发布于 2021-03-07 01:32
10111怎么旋转得来的?根据题目旋转的规则无论怎么旋转都得不到这个结果吧
2
送花
回复
分享
发布于 2020-07-09 00:11
这个解释感觉不太友好...
2
送花
回复
分享
发布于 2020-10-12 15:37
书上原话是“递增”,这里换成了“非递减”,这是两个完全不同的概念,但是解法居然是一样的。官方的回答很难让人相信正确性与权威性,建议直接看书
2
送花
回复
分享
发布于 2021-06-10 19:13
[1,2,2,2,2,2]这个数组是怎么出现的?哪里旋转了?
2
送花
回复
分享
发布于 2021-08-02 20:28
非递减序列,没法用二分法的吧。或者说不能单纯的用二分法,等值的时候就要额外写判断逻辑了吧。
1
送花
回复
分享
发布于 2020-12-03 16:49
非递减排序 应该要考虑=的情况吧 感觉这个答案有点纰漏
点赞
送花
回复
分享
发布于 2020-09-14 21:02
python这么写超时了
点赞
送花
回复
分享
发布于 2020-11-24 19:40
感觉不太对,“情况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” 这个时候如果序列为,416223.同样也是非递减的。但答案就不在 [mid+1...last]区间里
点赞
送花
回复
分享
发布于 2020-12-23 10:57
10111过不了啊
点赞
送花
回复
分享
发布于 2020-12-25 17:23
最后一种情况也可以另last=mid,这样什么情况下都是o(logN)的复杂度
点赞
送花
回复
分享
发布于 2021-02-04 17:26
题目没有说一开始的数组是递增 那么非递减排序的旋转就有可能是 132789的旋转789132 对这样的数组进行取最小 你上面的解法不对吧
点赞
送花
回复
分享
发布于 2021-05-13 16:46
而且按照你的解法 5678912这种的数组 你的解法也是不对了
点赞
送花
回复
分享
发布于 2021-05-13 16:48
既然非递减排序,那么为啥不直接遍历,找第一个小于当前节点的值呢
点赞
送花
回复
分享
发布于 2021-06-30 22:04
```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

相关推荐

155 19 评论
分享
牛客网
牛客企业服务