前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >二分查找经典题目

二分查找经典题目

作者头像
用户11039529
发布2024-09-24 19:15:12
1140
发布2024-09-24 19:15:12
举报
文章被收录于专栏:算法学习日常

二分查找

什么时候能用二分查找?

当数据具有“二段性”的时候,

也就是根据某个规律,能将数据分为两个部分,又根据某个规律可以将其中一部分给舍弃

朴素的二分查找:

1、循环条件?

left <= right

只有left > right时才能跳出循环,因为每次分割出来的区间的数是未知的,当left == right的时候仍然要检查

朴素版本二分查找模板
代码语言:javascript
复制
while (l <= r)
{
    // 有溢出风险,如果l和r的和 > int,会溢出
    // mid = (l + r) / 2;

    // 以下两个计算mid的方法是防止溢出的
    // 如果数据量为偶数时,更偏向于找左边的数的下标
    mid = l + (r - l) / 2;
    // 让l走[l, r]的一半

    // 如果数据量为偶数时,更偏向于找左边的数的下标
    mid = l + (r - l + 1) / 2;

    if (……)
    {
        r = mid - 1;
    }
    else if (……)
    {
        l = mid + 1;
    }
    else
        return ……;

}

省略号部分是根据不同情况下的二段性来填写的

704. 二分查找

704. 二分查找 - 力扣(LeetCode)

代码语言:javascript
复制
class Solution 
{
public:
    int search(vector<int>& nums, int target) 
    {
        int l = 0, r = nums.size() - 1, mid;
        while (l <= r)
        {
            // 有溢出风险,如果l和r的和 > int,会溢出
            // mid = (l + r) / 2;

            // 以下两个计算mid的方法是防止溢出的
            // 如果数据量为偶数时,更偏向于找左边的数的下标
            mid = l + (r - l) / 2;
            // 让l走[l, r]的一半

            // 如果数据量为偶数时,更偏向于找左边的数的下标
            mid = l + (r - l + 1) / 2;


            if (nums[mid] > target)
            {
                r = mid - 1;
            }
            else if (nums[mid] < target)
            {
                l = mid + 1;
            }
            else
                return mid;
        }
        return -1;
    }
};

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

题目链接:34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)

代码语言:javascript
复制
class Solution 
{
public:
    vector<int> searchRange(vector<int>& nums, int target) 
    {
        if (nums.size() == 0)
            return {-1, -1};

        int ansl = 0, ansr = 0;    

        // 求左端点
        int l = 0, r = nums.size() - 1, mid;
        while (l </**/ r)
        {/*
            1、=的情况可以直接判断,无需继续循环
            2、容易死循环,
            因为求左端点时当target >= mid,则right = mid
            因为求右端点时当target <= mid,则left = mid
        */
            mid = l + (r - l/*求偏左边的点*/) / 2;
            if (nums[mid] < target)
            {
                l = mid + 1;
            }
            else if (nums[mid] >=/**/ target)
            {
                r = mid;// 因为可能mid这个点就是左端点
            }
        }
        if (nums[l] != target)
            return {-1, -1};
        else
            ansl = l;

         // 求右端点
        l = 0, r = nums.size() - 1;
        while (l </**/ r)
        {/*
            1、=的情况可以直接判断,无需继续循环
            2、容易死循环,
            因为求左端点时当target >= mid,则right = mid
            因为求右端点时当target <= mid,则left = mid
        */
            mid = l + (r - l + 1/*求偏右边的那个点*/) / 2;
            if (nums[mid] <=/**/ target)
            {
                l = mid;// 因为可能mid这个点就是右端点
            }
            else if (nums[mid] > target)
            {
                r = mid - 1;
            }
        }
        if (nums[l] != target)
            return {-1, -1};
        else
            ansr = l;

        return {ansl, ansr};
    }
};

求左端点模板

代码语言:javascript
复制
       while (l </**/ r)
       {/*
            1、=的情况可以直接判断,无需继续循环
            2、容易死循环,
            因为求左端点时当target >= mid,则right = mid
            因为求右端点时当target <= mid,则left = mid
        */

            mid = l + (r - l/*求偏左边的点,否则可能死循环,因为l一直更新为 = mid*/) / 2;

            if (nums[mid] < target)// (……)括号里面填写“二段性”的相关判断条件
            {
                l = mid + 1;
            }
            else if (nums[mid] >=/**/ target)// (……)括号里面填写“二段性”的相关判断条件
            {
                r = mid;// 因为可能mid这个点就是左端点
            }
        }
        if (nums[l] != target)
            return {-1, -1};
        else
            ansl = l;

求右端点模板

代码语言:javascript
复制
        while (l </**/ r)
        {/*
            1、=的情况可以直接判断,无需继续循环
            2、容易死循环,
            因为求左端点时当target >= mid,则right = mid
            因为求右端点时当target <= mid,则left = mid
        */

            mid = l + (r - l + 1/*求偏右边的那个点,否则可能死循环,因为l一直更新为 = mid*/) / 2;

            if (nums[mid] <=/**/ target)// (……)括号里面填写“二段性”的相关判断条件
            {
                l = mid;// 因为可能mid这个点就是右端点
            }
            else if (nums[mid] > target)// (……)括号里面填写“二段性”的相关判断条件
            {
                r = mid - 1;
            }
        }

69. x 的平方根

69. x 的平方根 - 力扣(LeetCode)

二段性: 1、平方 > x 2、平方 <= x

代码语言:javascript
复制
// 求左端点的情况
class Solution 
{
public:
    int mySqrt(int x) 
    {
        if (x < 1)
            return 0;// 边界情况
        int l = 1, r = x;
        long long mid;// 因为x <= 2^31 - 1, mid * mid可能溢出
        while (l < r)
        {
            mid = l + (r - l + 1) / 2;
            if (mid * mid <= x)
                l = mid;
            else// if (mid * mid > x)
                r = mid - 1;// 有 - 1 -> mid计算时要+ 1
        }
        
        return l;
    }
};

35. 搜索插入位置

题目链接:. - 力扣(LeetCode)

该题关键点:找第一个大于target的位置 -------> 二段性:(1) < target (2) >= target 左端点的解法

代码语言:javascript
复制
class Solution 
{
public:
    int searchInsert(vector<int>& nums, int target) 
    {
        int l = 0, r = nums.size() - 1, mid;
        while (l < r)
        {
            mid = l + (r - l) / 2;
            if (nums[mid] < target)
                l = mid + 1;
            else if (nums[mid] >= target)
                r = mid;
        }

        if (nums[l] < target)
            return l + 1;
        return l;
    }
};

852. 山脉数组的峰顶索引

题目链接:. - 力扣(LeetCode)

总体先递增再递减 ----> 二段性:左半部分(arr[mid - 1] <= arr[mid]),右半部分(arr[mid - 1] > arr[mid])

代码语言:javascript
复制
class Solution {
public:
    int peakIndexInMountainArray(vector<int>& arr) 
    {
        int l = 0, r = arr.size() - 1, mid;
        while (l < r)
        {
            mid = l + (r - l + 1) / 2;
            if (mid > 0)
            {
                if (arr[mid - 1] <= arr[mid])
                    l = mid;// 因为mid可能就是答案
                else
                    r = mid - 1;// 有 - 1 -----> 上面要 + 1
            }
            else
                break;
        }

        return r;
        // return l;// 都可以
    }
};

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

题目链接:153. 寻找旋转排序数组中的最小值 - 力扣(LeetCode)

特点:递增(设为AB段)、递增(设为CD段) 二段性: AB段:nums[i] > nums[n - 1] CD段:nums[i] <= nums[n - 1] 该解法用的是D来作为参照

代码语言:javascript
复制
class Solution {
public:
    int findMin(vector<int>& nums) 
    {
        int size = nums.size();
        int l = 0, r = size - 1, mid;
        while (l < r)
        {
            mid = l + (r - l) / 2;
            if (nums[mid] > nums[size - 1])// AB段
                l = mid + 1;
            else
                r = mid;
        }

        return nums[r];// 注意要返回的是 最小   元素   ,不是元素  下标
    }
};

LCR 173. 点名

题目链接:LCR 173. 点名 - 力扣(LeetCode)

O(n):1、哈希表 2、直接遍历 3、位运算(和正确数组位运算) 4、求和(用等差数列求正确的和,再减去题目所给元素和) O(logN):二分查找 二段性: 在答案位置的前面,正确的学号 - 数组中的学号 = 0 在答案位置的后面,正确的学号 - 数组中的学号 = 1

代码语言:javascript
复制
class Solution {
public:
    int takeAttendance(vector<int>& records) 
    {
        int l = 0, r = records.size() - 1, mid;
        while (l < r)
        {
            mid = l + (r - l) / 2;
            if (records[mid] - mid == 0)
                l = mid + 1;
            else// if (records[mid] - mid == 1)
                r = mid;
        }

        return records[l] == l ? l + 1 : l;
    }
};
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-09-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 二分查找
    • 什么时候能用二分查找?
      • 朴素的二分查找:
        • 朴素版本二分查找模板
    • 704. 二分查找
    • 34. 在排序数组中查找元素的第一个和最后一个位置
      • 求左端点模板
        • 求右端点模板
        • 69. x 的平方根
        • 35. 搜索插入位置
        • 852. 山脉数组的峰顶索引
        • 153. 寻找旋转排序数组中的最小值
        • LCR 173. 点名
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档