【LeetCode刷题week1】——二分查找

二分查找专题

算法分析

【LeetCode刷题week1】——二分查找_第1张图片

模板一 :

  • Find: 右区间的左端点
  • 例如Find x 时,找第一个大于等于 x 的位置
int bsearch_1(int l, int r){
    while (l < r){
        int mid = (l + r) >> 1;  // mid = l + ((r-l) >> 1)
        if (check(mid)) r = mid;    // check()判断mid是否满足性质
        else l = mid + 1;
    }
    return l;
}

模板二 :

  • Find: 左区间的右端点
  • 例如Find x 时,找最后一个小于等于 x 的位置
int bsearch_2(int l, int r){
    while (l < r)    {
        int mid = (l + r + 1) >> 1; // 必须 + 1, 若溢出 改为long long
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}

1.题目链接

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

输入 / 输出样例

输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
---------------------
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
---------------------
输入:nums = [2,2], target = 3
输出:[-1,-1]

C++ 代码如下:

方法一:利用二分算法模板

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        if(nums.empty()) return {-1, -1};
        int l = 0, r = nums.size() - 1;
        // 模板一
        while(l < r){ // 找 >= x 的第一个数 ——————> 用模板一
            int mid = (l + r) / 2;
            if(nums[mid] >= target) r = mid;
            else l = mid + 1;
        }
        if(nums[l] != target) return {-1, -1}; // 没找到
        int start = l;
        // 模板二
        l = 0, r = nums.size() - 1;
        while(l < r){  // 找 <= x 的最后一个数 ——————> 用模板二
            int mid = l + r + 1 >> 1;
            if(nums[mid] <= target) l = mid;
            else r = mid - 1;
        }
        int end = r;
        return {start, end};
    }
};

方法二:利用库函数 lower_bound 和 upper_bound

  • 这两个库函数在algorithm头文件下,定义如下(图源 算法笔记——胡凡):
    【LeetCode刷题week1】——二分查找_第2张图片
  • 需要注意的是,返回的是位置
  • 如果是容器,则返回该位置的迭代器,因此要想获得位置,需要减去nums.begin()

int start = lower_bound(nums.begin(), nums.end(), target) - nums.begin();
int end = upper_bound(nums.begin(), nums.end(), target) - nums.begin() - 1

完整代码如下

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        if(nums.empty()) return {-1, -1};
        int start = lower_bound(nums.begin(), nums.end(), target) - nums.begin();

        if(start >= nums.size()) return {-1, -1}; // 没找到

        int end = upper_bound(nums.begin(), nums.end(), target) - nums.begin() - 1;
        
        if(end < start) return {-1, -1}; 
        // 特殊判断。因为upper和lower都是 > x, end是-1得到的
        // 如[5,7,7,8,8,10]找6
        // start = 1
        // end = 0
        return {start, end};
    }
};

2.题目链接

LeetCode74. 搜索二维矩阵

输入 / 输出样例

输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 11
输出:true

代码

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        if(matrix.empty() || matrix[0].empty()) return false;
        int n = matrix.size(), m = matrix[0].size();
        int l = 0, r = m * n - 1; // 将数组展开为序列
        while(l < r){
            int mid = l + r >> 1;
            if(matrix[mid / m][mid % m] >= target) r = mid;
            // 二维坐标 转 一维坐标
            // i = k / col
            // j = k % col
            else l = mid + 1; 
        }
        if(matrix[l/m][l%m] != target) return false; // 没找到
        return true;
    }
};

3.题目链接

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

输入 / 输出样例

输入:nums = [4,5,6,7,0,1,2]
输出:0

【LeetCode刷题week1】——二分查找_第3张图片
代码

class Solution {
public:
    int findMin(vector<int>& nums) {
        int l = 0, r = nums.size()-1;
        while(l < r){
            int mid = (l + r)/2;
            if(nums[mid] <= nums.back()) // 小于等于末尾
                r = mid;
            else l = mid + 1;
        }
        return nums[l];
    }
};

4.题目链接

LeetCode33. 搜索旋转排序数组

输入 / 输出样例

输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4
----------------
输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1

代码

class Solution {
public:
    int search(vector<int>& nums, int target) {
        if(nums.empty()) return -1;
        // 找min
        int l = 0, r = nums.size() - 1;
        while(l < r){
            int mid = l + r >> 1;
            if(nums[mid] <= nums.back()) r = mid;
            else l = mid + 1;
        } // 当前l, r都是min的下标

        if(target <= nums.back()){ // 则在右区间进行二分查找
            r = nums.size() - 1; // l = l
        }// 否则在左区间进行二分查找
        else l = 0, r--;   // r = l - 1 = r--
        while(l < r){
            int mid = l + r >> 1;
            if(nums[mid] >= target) r = mid;
            else l = mid + 1;
        }

        if(nums[l] != target) return -1;
        return l; 
    }
};

5.题目链接

LeetCode162. 寻找峰值

输入 / 输出样例

输入:nums = [1,2,1,3,5,6,4]
输出:15 

【LeetCode刷题week1】——二分查找_第4张图片

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

6.题目链接

LeetCode287. 寻找重复数

【LeetCode刷题week1】——二分查找_第5张图片

输入 / 输出样例

输入:nums = [3,1,3,4,2]
输出:3

代码

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int l = 1, r = nums.size()-1;
        while(l < r){
            int mid = (l + r) / 2;
            int cnt = 0;
            for(auto i : nums){
                if(i >= l && i <= mid) cnt ++;
            }
            if(cnt > mid - l + 1) r = mid; // cnt > 左区间元素个数,则从左区间找
            else l = mid + 1;
        }
        return l;
    }
};

7.题目链接

LeetCode275. H 指数 II

【LeetCode刷题week1】——二分查找_第6张图片

输入 / 输出样例

输入:citations = [0,1,3,5,6]
输出:3 

代码

class Solution {
public:
    int hIndex(vector<int>& c) {
        int n = c.size();
        int l = 0, r = n; // h的大小一定不超过数组长度
        while(l < r){
            int mid = (l+r+1)/2;
            if(c[n - mid] >= mid) l = mid;
            else r = mid - 1;
        }
        return l;
    }
};

你可能感兴趣的:(LeetCode专题,leetcode,算法,二分查找,二分法,c++)