二分查找——OJ题(一)

简介: 二分查找——OJ题(一)

一、二分查找


1、题目讲解

9682c6730eb942e8ba4a06812216f567.png

2、算法原理

a. 定义 left , right 指针,分别指向数组的左右区间。

b. 找到待查找区间的中间点 mid ,找到之后分三种情况讨论:

i. arr[mid] == target 说明正好找到,返回 mid 的值;

ii. arr[mid] > target 说明 [mid, right] 这段区间都是⼤于 target 的,因此舍去右边区间,在左边 [left, mid -1] 的区间继续查找,即让 right = mid - 1 ,然后重复 2 过程;

iii. arr[mid] < target 说明 [left, mid] 这段区间的值都是⼩于 target 的,因此舍去左边区间,在右边 [mid + 1, right] 区间继续查找,即让 left = mid + 1 ,然后重复 2 过程;

c. 当 left 与 right 错开时,说明整个区间都没有这个数,返回 -1 。


3、代码实现

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left=0,right=nums.size()-1;
        while(left<=right)
        {
            int mid=left+(right-left)/2;
            if(nums[mid]<target) left=mid+1;
            else if(nums[mid]>target )right=mid-1;
            else return mid;
        }
       return -1;
    }
};


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


1、题目讲解

b6f632b8c57e4fbaa5a8b643ff04c8ed.png


2、算法原理

⽤的还是⼆分思想,就是根据数据的性质,在某种判断条件下将区间⼀分为⼆,然后舍去其中⼀个区间,然后再另⼀个区间内查找;

⽅便叙述,⽤ x 表⽰该元素, resLeft 表⽰左边界, resRight 表⽰右边界。

寻找左边界思路:

• 寻找左边界:

◦ 我们注意到以左边界划分的两个区间的特点:

▪ 左边区间 [left, resLeft - 1] 都是⼩于 x 的;

▪ 右边区间(包括左边界) [resLeft, right] 都是⼤于等于 x 的;

• 因此,关于 mid 的落点,我们可以分为下⾯两种情况:

◦ 当我们的 mid 落在 [left, resLeft - 1] 区间的时候,也就是 arr[mid] < target 。说明 [left, mid] 都是可以舍去的,此时更新 left 到 mid + 1 的位置,

继续在 [mid + 1, right] 上寻找左边界;

◦ 当 mid 落在 [resLeft, right] 的区间的时候,也就是 arr[mid] >= target 。

说明 [mid + 1, right] (因为 mid 可能是最终结果,不能舍去)是可以舍去的,此时

更新 right 到 mid 的位置,继续在 [left, mid] 上寻找左边界;

• 由此,就可以通过⼆分,来快速寻找左边界;

注意:这⾥找中间元素需要向下取整。

因为后续移动左右指针的时候:

• 左指针: left = mid + 1 ,是会向后移动的,因此区间是会缩⼩的;

• 右指针: right = mid ,可能会原地踏步(⽐如:如果向上取整的话,如果剩下 1,2 两个元

素, left == 1 , right == 2 , mid == 2 。更新区间之后, left,right,mid 的

值没有改变,就会陷⼊死循环)。

因此⼀定要注意,当 right = mid 的时候,要向下取整。


寻找右边界思路:

• 寻右左边界:

◦ ⽤ resRight 表⽰右边界;

◦ 我们注意到右边界的特点:

▪ 左边区间 (包括右边界) [left, resRight] 都是⼩于等于 x 的;

▪ 右边区间 [resRight+ 1, right] 都是⼤于 x 的;

• 因此,关于 mid 的落点,我们可以分为下⾯两种情况:

◦ 当我们的 mid 落在 [left, resRight] 区间的时候,说明 [left, mid - 1]( mid 不可以舍去,因为有可能是最终结果) 都是可以舍去的,此时更新 left 到 mid的位置; 当 mid 落在 [resRight+ 1, right] 的区间的时候,说明 [mid, right] 内的元素

是可以舍去的,此时更新 right 到 mid - 1 的位置;

• 由此,就可以通过⼆分,来快速寻找右边界;

注意:这⾥找中间元素需要向上取整。

因为后续移动左右指针的时候:

• 左指针: left = mid ,可能会原地踏步(⽐如:如果向下取整的话,如果剩下 1,2 两个元素, left == 1, right == 2,mid == 1 。更新区间之后, left,right,mid 的值

没有改变,就会陷⼊死循环)。

• 右指针: right = mid - 1 ,是会向前移动的,因此区间是会缩⼩的;

因此⼀定要注意,当 right = mid 的时候,要向下取整。


3、代码实现

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        if(nums.size()==0)  return {-1,-1};
        int left=0,right=nums.size()-1,begin=0;
        while(left<right)
        {
            int mid=left+(right-left)/2;
            if(nums[mid]<target) left=mid+1;
            else right=mid;
        }
        if(nums[left]!=target) return{-1,-1};
        else begin=left;
        left=0,right=nums.size()-1;
        while(left<right)
        {
            int mid=left+(right-left+1)/2;
            if(nums[mid]<=target) left=mid;
            else right=mid-1;
        }
        return {begin,right};
    }
};


三、X的平方根


1、题目讲解

ef0c3d45260541368071dde914a64cac.png


2、算法原理

设 x 的平⽅根的最终结果为 index :

a. 分析 index 左右两次数据的特点:

▪ [0, index] 之间的元素,平⽅之后都是⼩于等于 x 的;

▪ [index + 1, x] 之间的元素,平⽅之后都是⼤于 x 的。


3、代码实现

class Solution {
public:
    int mySqrt(int x) {
        if(x>=0 && x<1)  return 0;
        int left=1,right=x;
        while(left<right)
        {
            long long mid=left+(right-left+1)/2;
            if(mid*mid<=x) left=mid;
            else right=mid-1;
        }
        return left;
    }
};


四、搜索插入位置


1、题目讲解

58b3da736bc040148b99674c802174e0.png

2、算法原理

a. 分析插⼊位置左右两侧区间上元素的特点:

设插⼊位置的坐标为 index ,根据插⼊位置的特点可以知道:

• [left, index - 1] 内的所有元素均是⼩于 target 的;

• [index, right] 内的所有元素均是⼤于等于 target 的。

b. 设 left 为本轮查询的左边界, right 为本轮查询的右边界。根据 mid 位置元素的信

息,分析下⼀轮查询的区间:

▪ 当 nums[mid] >= target 时,说明 mid 落在了 [index, right] 区间上,

mid 左边包括 mid 本⾝,可能是最终结果,所以我们接下来查找的区间在 [left, mid] 上。因此,更新 right 到 mid 位置,继续查找。

▪ 当 nums[mid] < target 时,说明 mid 落在了 [left, index - 1] 区间上,

mid 右边但不包括 mid 本⾝,可能是最终结果,所以我们接下来查找的区间在 [mid+ 1, right] 上。因此,更新 left 到 mid + 1 的位置,继续查找。

c. 直到我们的查找区间的⻓度变为 1 ,也就是 left == right 的时候, left 或者right 所在的位置就是我们要找的结果。


3、代码实现

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int left=0,right=nums.size()-1;
        while(left<right)
        {
            int mid=left+(right-left)/2;
            if(nums[mid]<target) left=mid+1;
            else right=mid;
        }
        if(nums[left]<target) return left+1;
        else return left;
    }
};


目录
相关文章
|
6月前
|
算法
二分查找——OJ题(二)
二分查找——OJ题(二)
73 0
|
25天前
【LeetCode 01】二分查找总结
【LeetCode 01】二分查找总结
12 0
|
6月前
|
算法 测试技术 C#
[二分查找双指针]LeetCode881: 救生艇
[二分查找双指针]LeetCode881: 救生艇
|
6月前
|
Java C++ Python
leetcode-704:二分查找
leetcode-704:二分查找
40 0
|
算法
【LeetCode 算法专题突破】二分查找(⭐)
【LeetCode 算法专题突破】二分查找(⭐)
35 0
|
索引
【剑指Offer】--->详解二分查找相关练习(二)
【剑指Offer】--->详解二分查找相关练习(二)
72 1
【剑指Offer】--->详解二分查找相关练习(一)
【剑指Offer】--->详解二分查找相关练习(一)
57 0
|
机器学习/深度学习 算法 安全
LeetCode刷题系列(二)二分查找、二叉排序树 的应用
LeetCode刷题系列(二)二分查找、二叉排序树 的应用
|
存储
力扣:二分查找的相关题
力扣:二分查找的相关题