• 二分问题总结


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

    循环条件为(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-/

  • 相关阅读:
    Ad hoc access to OLE DB provider 'Microsoft.ACE.OLEDB.12.0' has been denied. You must access this provider through a linked server.
    阻塞问题:会话是sleeping的,但是open_tran 不是0
    windows Server DNS服务器配置
    内存缺页
    "ros::NodeHandle"的用法:全局vs.私有
    python 判断当前执行用户是否是 root 用户
    docker 安装及启动 postgresql 及navicat 连接
    Mac 在环境变量中隐藏密码或者密钥等信息
    磁盘空间不足导致虚拟机无法启动
    VirtuaBox 下安装 Centos8 无法上网
  • 原文地址:https://www.cnblogs.com/Aliencxl/p/12298462.html
Copyright © 2020-2023  润新知