剑指 Offer 11. 旋转数组的最小数字

发布时间 2023-09-06 17:56:14作者: luxiayuai

本题的作法是二分法。具体做法是:左右区间根据number[r](右端点)进行区分,利用左区间大于等于number[r],右区间小于等于number[r]的特性。

在此基础上,二分法得以适用。

本题的一个大坑:

二分法的中点,numbers[mid],能否与numbers[l](左端点)作比较?

答案是:可以,但是要特别防范最小值就是numbers[l]的情况。

本题最好与numbers[r]作比较。

class Solution {
public:
    int minArray(vector<int>& numbers) {
        if(numbers.size() == 0) return 0;
        int l = 0, r = numbers.size() - 1;
        while(l < r) {
            int mid = l + (r - l) / 2;
            // 左右区间根据number[r]进行区分,左区间大于等于number[r],右区间小于等于number[r]的特性
            if(numbers[mid] < numbers[r]) r = mid;
            else if(numbers[mid] > numbers[r]) l = mid + 1;
            else r -= 1;
        }
        return numbers[l];
    }
};                 

如果与numbers[l](左端点)进行比较,也是可以的,但是每次循环都要特别判断最小值就在区间最左边的情况(例如[1 2 3 4 5]):

class Solution {
public:
    int minArray(vector<int>& numbers) {
        if(numbers.size() == 0) return 0;
        int l = 0, r = numbers.size() - 1;
        while(l < r) {
            // 如果用numbers[l]来区分,则区分依据还是左区间大于等于number[l],右区间小于等于number[l]
            // 但是如果最小值就在number[l]处,例如[1 2 3 4 5],那么计算出的number[mid]一定是大于numbers[l]的,但是此时numbers[mid]在右区间,与区分依据违背
            // 因此,以numbers[l]作区分依据需要每次循环都判断numbers[l]<numbers[r]。
            if(numbers[l] < numbers[r]) return numbers[l];
            int mid = l + (r - l) / 2;
            if(numbers[mid] < numbers[l]) r = mid;
            else if(numbers[mid] > numbers[l]) l = mid + 1;
            else l = l + 1; 
        } 
        return numbers[l];
    }
};        

tips:

// 当l和r非常大时,mid有可能会溢出
mid = (l + r) / 2
// 解决方案:
mid = l + (r - l) / 2