zoukankan      html  css  js  c++  java
  • [算法笔记]二分总结

    一、什么时候使用二分?:

    ​ 大部分人接触二分应该二分查找算法,二分查找可以在一个有序区间中,找到指定的元素x的下标。

    这里有两点:1、区间是有序的,即区间要满足一定条件。2、我们要找的数也有一定条件,二分查找要求找的数一定要等于x。

    ​ 我们再看两题:

    AcWing.789.数的范围

    题目给出的是按照升序排列的整数数组,要求一个元素k的起始终止位置,也就是第一个等于k的数和最后一个等于k的数。

    LeeCode.852. 山脉数组的峰顶索引

    题目给出一个山脉数组,即在峰顶元素左边升序排列,右边降序,可以算是半有序的状态。要找的就是峰顶元素。
    可以看到,都满足我们提出的两个条件。当我们看到一个有一定条件分布的数组,要我们寻找一个较为特殊的值时,我们就可以考虑使用二分来做。

    二、二分模板:

    ​ 大部分人最早接触二分算法应该都是二分查找算法,但是二分能做的远不止与此。二分算法的核心在于一个check(mid)函数,只要这个函数能够将给定的序列划分为两个区间,一个满足check(mid),一个不满足。就可以根据模板查找目标值。

    ​ 二分总体上可以分为两个模板,需要根据情况选择使用。

    1、模板一:

    int binary_search() {
        int l = 0, r = n - 1;
        while (l < r) {
            int mid = (l + r) / 2;
            if (check(mid)) r = mid;
            else l = mid + 1;
        }
        return l;
    }
    

    2、模板二:

    int binary_search() {
        int l = 0, r = n - 1;
        while (l < r) {
            int mid = (l + r + 1) / 2;
            if (check(mid)) l = mid;
            else r = mid - 1;
        }
        return l;
    }
    

    ​ 两个模板的关键都在4~6行,即如何划分区间。模板一会将区间划为([l, mid]和[mid+1, r]),而模板二会将区间划为([l, mid-1]和[mid, r])这就是模板最重点的区别。

    ​ 模板上实际也是可以互换的

    • 两个模板最后返回时,都有(l ==r),且(l、r)都等于划分的第一个区间的右端点,即mid或mid - 1。

    • 模板二求mid时需要+1,这是为了防止死循环。

    三、模板使用:

    ​ 下面画图说明两个模板的区别和应用:

    ​ 如果只想记住模板直接使用,有一个很好理解的方法。我们设xxx表示不满足check(mid)的序列元素,ooo表示满足,v表示我们需要的数。假设题目要求找到0到n之间的>5的第一个数,我们的序列类似这样。

    0 1 2 3 4 5 6 7 8 9 10 ... n

    x x x x x x v o o o o ... o

    ​ 可以看到,序列被划分成左边不满足、右边满足。我们要找的数就是第一个满足check(mid)的值,面对这样的划分区间,就可以套用模板一。

    ​ 假设要找 <= 5的最后一个数,我们的序列如下:

    0 1 2 3 4 5 6 7 8 9 10 ... n

    o o o o o v x x x x x ... x

    序列左边满足、右边不满足check(mid)。v就是最后一个满足check(mid)的值,这样的划分区间就可以套用模板二。

    ​ 最后总结一下二分的思路:

    1. 首先把序列写出来,判断题目要求的是满足某一要求的第一个值而是最后一个值。
    2. 接着根据要求画出这样的划分情况分析。
    3. 如果形如x x x x v o o o,选择模板一,形如o o o v x x x,选择模板二。

    三、思考:

    ​ 我们知道模板一模板二最后的l、r值是相同的,在模板一、二中分别等于mid、mid-1。两个模板也是可以相互转换的,第一个不满足check(mid)的值也就是最后一个满足check(mid)的值的下一个值。因此模板二的check(mid)改称与模板一的完全相反的判断函数,最后模板二返回l+1,就可以得到与模板一相同的答案。但是不建议这样使用。

    ​ 其次check(mid)函数选择还是有一定技巧的,如果不考虑像上面那样更改模板。那么check(mid)就决定了我们该使用哪个模板。

  • 相关阅读:
    .net core2.2
    9_山寨系统调用 SystemCallEntry
    7_API调用
    8_InlineHook
    6_再次开中断STI的正确姿势
    5_中断现场下
    4_中断现场中
    3_中断现场上
    2_多核复杂性
    1_中断提权
  • 原文地址:https://www.cnblogs.com/enmac/p/13874227.html
Copyright © 2011-2022 走看看