【剑指offer】面试题11-旋转数组中的最小数字(二分法的进一步应用)

题目

题目剖析:
按照书上的解法:
旋转后的数组可以分为两个有序子数组,不考虑重复数字的情况下,前面的数组都要大于后面的数组。这是找的就是后面数组中的第一个数字。如果有重复的数字,只能顺序查找了。

思路: (双指针解法,二分法)

  1. 直接用一个指针指向第一个元素,一个指针指向最后一个元素。没有重复元素的话,第一个元素是大于最后的元素的。
  2. 在让mid = (left + right )/ 2 的元素,若中间的元素大于第一个元素,则中间的元素在前面递增子数组中,这时就可让left指针指向中间元素
  3. 若中间的元素小鱼第一个元素,则中间的元素位于后面的递增数组中,这时就可以让right指针指向中间的元素。
  4. 这样一直查找,最后找到的最小元素就是第二的子数组的第一个元素。

牛客网AC代码如下

class Solution {
public:
    int MinInOrder(vector<int> rotateArray, int left, int right) {
        int re = rotateArray[left];
        for (int i = left + 1; i <= right; ++i) {
            if (re > rotateArray[i]) re = rotateArray[i];
        }
        return re;
    }
 
    // 找中位数2版本  (牛客网可AC,1, 0, 1, 1, 1 测试通过)
    int minNumberInRotateArray(vector<int> rotateArray) {
        if (rotateArray.size() <= 0) return 0;
 
        int left = 0;
        int right = rotateArray.size() - 1;
        int mid = left;
        while (rotateArray[left] >= rotateArray[right]) {
            if (right - left == 1) {
                mid = left;
                break;
            }
            mid = left + (right - left) / 2;
            // 下标指向的数字相同,则按照顺序查找
            if (rotateArray[left] == rotateArray[right] && rotateArray[mid] == rotateArray[left])
                return MinInOrder(rotateArray, left, right);
            if (rotateArray[mid] >= rotateArray[left])  left = mid;
            if (rotateArray[mid] <= rotateArray[right])  right = mid;
        }
        return rotateArray[right];
    }
};

现在我主要记录二分的进一步应用
二分法的使用范围: 不一定是有序的,这里旋转数组,将有序的数组打乱,我们照样可以用二分法将这个分界点找到。

1. 因为题目中可能出现重复的数字,如果是从重复的数组中间开始旋转,那么后面数组的重复元素则不满足二分性质。所以我们要将后面重复的元素删除。这样就能保证后面的元素都小于前面的元素。不能要等于,所以删除。
2. 删除后,如果最后一个元素和第一个元素比较,大于的话,就是整个单调数组,这是直接返回第一个元素即可
3. 现在就可以用二分查找了。同样left, right 一个指向第一个元素,一个指向最后一个元素。
4. 在mid和第一个元素比较,如果中间的元素比第一个元素大, 则让left指向mid + 1的元素
5. 如果中间的元素比第一个元素小,则让right指向mid的元素。
6. 那么等于的元素放到哪里呢,因为我们将后面等于第一个元素的值删除了,所以这是还等于的话,直接是在前面的数组,所以等于的话,直接让left指向mid+ 1 的元素

7. 下面就看代码

class Solution {
public:
    int minNumberInRotateArray(vector<int> arr) {
        int n = arr.size() - 1;
        if(n<=0) return -1;
        while(n > 0 && arr[n] == arr[0]) n--;
        // 考虑单调情况
        if(arr[n] >= arr[0]) return arr[0];
        int l = 0, r = n;
        while(l < r) {
            int mid = l+r >> 1;
            // 下面一定要>= ,只是> 的话,经测试只能通过90的例子
            if(arr[mid] >= arr[0]) l = mid + 1;  // 这里是跟arr[0]比较大小, 
            else
                r = mid;
        }
        return arr[r];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值