二分问题总结

二分问题可以按循环条件总结为两个模板:

循环条件为(left <= right):

while(left <= right) {
        int mid = (right + left) / 2;
        if(nums[mid] == target)
            return mid; 
        else if (nums[mid] < target)
            left = mid + 1; // 注意
        else if (nums[mid] > target)
            right = mid - 1; // 注意
        }
    return -1;

如果是找小于等于target的第一个数,则返回right;

如果是找大于target的数,则返回left;

这样的题目有:

69. x 的平方根

实现 int sqrt(int x) 函数。

计算并返回 x 的平方根,其中 x 是非负整数。

由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。

class Solution {
public:
    int mySqrt(int x) {
        int l = 0, r = x;
        while(l <= r){
            int m = l + (r-l)/2;
            long long mm = m; mm*=mm; // 不能直接用mm= m*m;
            if(mm == x) return m;
            else if(mm >x) r = m-1;
            else l = m+1;
        }
        return r;
    }
};

744. 寻找比目标字母大的最小字母

给定一个只包含小写字母的有序数组letters 和一个目标字母 target,寻找有序数组里面比目标字母大的最小字母。

数组里字母的顺序是循环的。举个例子,如果目标字母target = 'z' 并且有序数组为 letters = ['a', 'b'],则答案返回 'a'。

class Solution {
public:
    char nextGreatestLetter(vector<char>& letters, char target) {
        int n = letters.size();
        int l = 0, r = letters.size() -1;
        while(l <= r){
            int m = l + (r-l)/2;
            if(letters[m] <= target) l = m+1;//因为是找大于;
            else r = m-1;
        }
        return l<n? letters[l]: letters[0];
    }
};

第二种模板是排除法,有向左排除和向右排除:

int mid = left + (right - left) / 2;
if (check(mid)) {
    // 下一轮搜索区间是 [left, mid]
    right = mid;
} else {
    left = mid + 1;
}
int mid = left + (right - left + 1) / 2;
if (check(mid)) {
    // 下一轮搜索区间是 [left, mid - 1]
    right = mid - 1;
} else {
    left = mid;
}

540. 有序数组中的单一元素

给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。

class Solution {
public:
    int singleNonDuplicate(vector<int>& nums) {
        int n= nums.size();
        int l = 0, r = n-1; // r = n 则不行
        while( l < r){
            int m = l + (r-l)/2;
            if(m&1) --m; // 让m始终指向偶数个
            //元素在右边
            if(nums[m] == nums[m+1]) l = m+2;
            //元素在左边或者本身;
            else r = m;
        }
        return nums[l]; 
    }
};

278. 第一个错误的版本

你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。

假设你有 n 个版本 [1, 2, ..., n],你想找出导致之后所有版本出错的第一个错误的版本。

你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。

示例:

给定 n = 5,并且 version = 4 是第一个错误的版本。

调用 isBadVersion(3) -> false
调用 isBadVersion(5) -> true
调用 isBadVersion(4) -> true

所以,4 是第一个错误的版本。 
// Forward declaration of isBadVersion API.
bool isBadVersion(int version);

class Solution {
public:
    int firstBadVersion(int n) {
        int l = 1, r = n;
        while(l < r){
            int m = l + (r-l)/2;
            if(isBadVersion(m)) r = m;
            else l = m+1;
        }
        return l;
    }
};

153. 寻找旋转排序数组中的最小值

假设按照升序排序的数组在预先未知的某个点上进行了旋转。

( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

请找出其中最小的元素。

你可以假设数组中不存在重复元素。

class Solution {
public:
    int findMin(vector<int>& nums) {
        int n = nums.size();
        int l = 0, r = n-1;
        while(l < r){
            int m = l + (r - l)/2;
            if(nums[m] < nums[r]) r = m;
            else l = m+1;
        }
        return nums[l];
    }
};

34. 在排序数组中查找元素的第一个和最后一个位置

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

你的算法时间复杂度必须是 O(log n) 级别。

如果数组中不存在目标值,返回 [-1, -1]。

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int n = nums.size();
        if(n == 0) return {-1,-1};
        int l = 0,r = n-1;
        vector<int> res(2);
        while( l < r){
            int m = l + (r-l)/2;
            if(nums[m] >= target) r = m;
            else l = m+1;
        }
        if(nums[l] == target) res[0] = l;
        else res[0] = -1;
        l = 0; r = n-1;
        while( l < r){
            int m = l + (r-l+1)/2; // 注意
            if(nums[m] <= target) l = m;
            else r = m-1;
        }
        if(nums[r] == target) res[1] = r;
        else res[1] = -1;
        return res;
    }
};

35. 搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

你可以假设数组中无重复元素。

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int n = nums.size();
        int l = 0, r = n;// 注意,因为插入位置可能是最后一个位置;
        while( l < r){
            int m = l +(r-l)/2;
            if(nums[m] >= target) r = m;
            else l = m+1;
        }
        return l;
    }
};

参考资料:

https://github.com/CyC2018/CS-Notes/blob/master/notes/Leetcode%20%E9%A2%98%E8%A7%A3%20-%20%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE.md#1-%E6%B1%82%E5%BC%80%E6%96%B9

https://leetcode-cn.com/problems/search-insert-position/solution/te-bie-hao-yong-de-er-fen-cha-fa-fa-mo-ban-python-/

猜你喜欢

转载自www.cnblogs.com/Aliencxl/p/12298462.html