首页 > 编程语言 >算法学习笔记(3)——二分

算法学习笔记(3)——二分

时间:2022-12-09 22:11:48浏览次数:64  
标签:二分 int mid 笔记 满足 算法 区间 部分 性质

二分

二分法用于解决这样一类问题:一个区间能分成左右两部分,左半部分满足性质 \(A\),右半部分不满足性质 \(A\)。问题的目标是求解这两部分的分界点。

所以二分法和区间里有没有单调性其实没什么关系,但是很多问题里是从单调性导出了上面的性质,上面的性质才是一个问题能用二分法求解的最本质的性质。

二分法每次取区间的中间元素,通过判断区间中点元素 \(a[mid]\) 是否满足性质 \(A\) 就能断定求解目标是在 \(mid\) 的左边还是右边,从而每次把求解区间长度缩减一半。

特点

在整数二分问题里,需要考虑缩减区间时是否要保留 \(mid\) 这个点,也就是说 \(mid\) 是否可能作为解存在。

对于整数二分而言,“求分界点”也就是求左侧部分(满足性质 \(A\))的最后一个数,或者求右侧部分(不满足性质 \(A\))的第一个数。

二分过程结束后,\(l\) 或者 \(r\)(用哪个都行,因为最后它俩是相等的)的位置就是所求的位置。

求左侧部分(满足性质 \(A\))的最后一个数

由于是求左半部分的最后一个数,所以如果区间中点的数 \(a[mid]\) 是满足性质 \(A\) 的,那么 \(a[mid]\) 这个数就是左侧部分的数,它就有可能是“左侧部分的最后一个数”,因此这时候要把区间缩减成包括 \(mid\) 在内的右半部分,也就是从 \([l, r]\) 变成 \([mid, r]\),因此执行 \(l = mid\)。

如果区间中点的数 \(a[mid]\) 是不满足性质 \(A\) 的,那么 \(a[mid]\) 这个数就是右侧部分的数,所以下一次要把区间从 \([l, r]\) 变成 \([l, mid - 1]\),因此执行 \(r = mid - 1\)。

在这类问题下,由于涉及 \(l = mid\) 这个操作,所以“计算区间中点 \(mid\)”的这个操作要使用 \(mid = (l + r + 1) / 2\) 来计算。而不能使用 \(mid = (l + r) / 2\) 来计算,当 \(l\) 的值是 \(r - 1\) 的时候,\(mid = (l + r) / 2\) 计算出来的的值是 \(l\),如果执行了 \(l = mid\) 的操作,那么区间是没有变化的,会陷入死循环。

这是因为除法会自动做下取整,然而计算 \(mid\) 的方法在这类问题里需要是:

\[mid = \lceil \frac{l + r}{2} \rceil \]

所以才要用 \(mid = (l + r + 1) / 2\) 来计算上取整后的值。

求右侧部分(不满足性质 \(A\))的第一个数

由于求的是右侧部分的第一个数,所以如果区间中点的数 \(a[mid]\) 是满足性质 \(A\) 的,那么 \(a[mid]\) 这个数就是左侧部分的数,所以下一次要把区间 \([l, r]\) 变成 \([mid + 1, r]\),因此执行 \(l = mid + 1\)。

如果区间中点的数 \(a[mid]\) 是不满足性质 \(A\) 的,那么 \(a[mid]\) 这个数就是右侧部分的数,它就有可能是”右侧部分的第一个数“,因此这时候要把区间缩减成包括 \(mid\) 在内的左半部分,也就是从 \([l, r]\) 变成 \([l, mid]\),因此执行 \(r = mid\)。

在这类问题下,由于涉及 \(r = mid\) 这个操作,所以“计算区间中点 \(mid\)”的这个操作要用 \(mid = (l + r) / 2\) 来计算。

这是因为除法会自动做下取整,计算 \(mid\) 的方法在这类问题里也正好是:

\[mid = \lfloor \frac{l + r}{2} \rfloor \]

所以才要用 \(mid = (l + r) / 2\) 来计算下取整后的值。

题目链接:AcWing 789. 数的范围

#include <iostream>

using namespace std;

const int N = 100010;

int n, m;
int q[N];

int main()
{
    cin >> n >> m;
    
    for (int i = 0; i < n; i ++ ) cin >> q[i];
    
    while (m -- ) {
        int x;
        cin >> x;
        
        int l = 0, r = n - 1;
        while (l < r) {
            int mid = l + r >> 1;
            if (q[mid] >= x) r = mid;
            else l = mid + 1;
        }
        
        if (q[l] != x) cout << "-1 -1" << endl;
        else {
            cout << l << ' ';
            
            int l = 0, r = n - 1;
            while (l < r) {
                int mid = l + r + 1 >> 1;
                if (q[mid] <= x) l = mid;
                else r = mid - 1;
            }
            
            cout << r << endl;
        }
    }
    
    return 0;
}

标签:二分,int,mid,笔记,满足,算法,区间,部分,性质
From: https://www.cnblogs.com/I-am-Sino/p/16970124.html

相关文章

  • 算法学习笔记(4)——高精度
    高精度高精度高精度加法高精度减法高精度乘法高精度除法高精度加法题目链接:AcWing791.高精度加法题目描述给定两个正整数(不含前导0),计算它们的和。输入......
  • 算法学习笔记(5)——前缀和与差分
    前缀和与差分前缀和与差分一维前缀和差分一维前缀和前缀和可以用于快速计算一个序列的区间和,也有很多问题里不是直接用前缀和,但是借用了前缀和的思想。用\(s[i......
  • 算法学习笔记(2)——归并排序
    归并排序归并排序的思想是基于分治法,其思路是:将待排序区间平分成左右两半,左右两侧分别递归地做归并排序。将这两个有序的区间合并(每次落一个较小的下来),就将这个区间排......
  • 算法学习笔记(1)——快速排序
    快速排序算法思想快排的思想是基于分治法,其思路是:确定分界点:给定要排序的数组q,先在数组中找一个数作为分界点值x,分界点可以取左边界值x=q[l],可以取右边界值x=q[r],......
  • docker学习笔记
    Docker&CKA学习笔记第一章、容器存在的意义、优势、docker介绍1.1容器存在的意义1、上线流程繁琐开发->测试->申请资源->审批->部>测试等环节2、资源利用率低......
  • 小林笔记【面试】
    小林笔记【面试】​​前言​​​​推荐​​​​小林笔记【面试】​​​​最后​​推荐​​https://xiaolincoding.com/​​小林笔记【面试】​​操作系统笔记【面试】​​​......
  • crypto-gmsm国密算法库
    crypto-gmsm国密算法库一、开发背景crypto-gmsm国密算法库是国密商密算法(SM2,SM3,SM4)工具类封装,国产密码算法(国密算法)是指国家密码局认定的国产商用密码算法,目前主要使用......
  • 代码随想录算法训练营第三天 | 203.移除链表元素、707.设计链表、206.反转链表
    203. 移除链表元素tag:#链表leetcode地址:203. 移除链表元素代码:functionremoveElements(head:ListNode|null,val:number):ListNode|null{constnewH......
  • hutools密码算法库
    hutool密码算法库一、开发背景Hutool针对BouncyCastle做了简化包装,用于实现国密算法中的SM2、SM3、SM4。国密算法工具封装包括:非对称加密和签名:SM2摘要签名算法:SM3......
  • 【主色提取】模糊C均值(FCM )聚类算法和彩色图像快速模糊C均值( CIQFCM )聚类算法
    OverridetheentrypointofanimageIntroducedinGitLabandGitLabRunner9.4.Readmoreaboutthe extendedconfigurationoptions.Beforeexplainingtheav......