二分查找

96 阅读2分钟

模板

整数二分

  • 如果有单调性,一定可以二分;但是可以二分的题目,不一定有单调性
  • 所以二分的本质并不是单调性,而是寻找满足某种性质的边界

Snipaste_2023-01-12_23-23-42.png

  • 第一种情况是判断mid是否满足红色性质,寻找的是红色部分的边界

  • 第二种情况是判断mid是否满足绿色性质,寻找的是绿色部分的边界

  • 为什么第一种情况要+1

    • 考虑到 l = r - 1 的情况,此时的 mid = l (除法向下取整),check后为true,陷入死循环
  • C++

bool check(int x) {/* ... */} // 检查x是否满足某种性质

// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int binary_search_1(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;    // check()判断mid是否满足性质
        else l = mid + 1;
    }
    return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int binary_search_2(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}
  • Java
//检查x是否满足某种性质
public static boolean check(int x) {
    /*....*/
}

//区间[l, r]被划分为[l, mid]和[mid + 1, r]时使用
//寻找左边界
public static int binarch_search_1(int l, int r) {
    while (l < r) {
        int mid = l + r >> 1;
        if (check(mid)) {
            r = mid;
        } else {
            l = mid + 1;
        }
    }
    return l;
}

//区间[l, r]被划分为[l, mid - 1]和[mid , r]时使用
//寻找右边界 
public static int binarch_bsearch_2(int l, int r) {
    while (l < r) {
        int mid = l + r + 1 >> 1;
        if (check(mid)) {
            l = mid;
        } else {
            r = mid - 1;
        }
    }
    return l;
}

浮点数二分

  • 浮点数二分就不会出现边界问题

练习

01 数的范围

  • 题目

Snipaste_2023-01-15_22-53-00.png

  • 题解
import java.io.*;

public class Main {
    public static final int N = 100010;
    public static int[] arr = new int[N];

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String[] str1 = br.readLine().split(" ");
        int n = Integer.parseInt(str1[0]);
        int q = Integer.parseInt(str1[1]);
        String[] str2 = br.readLine().split(" ");
        for (int i = 0; i < n; i++) {
            arr[i] = Integer.parseInt(str2[i]);
        }

        while (q-- > 0) {
            int k = Integer.parseInt(br.readLine());
            int l = 0, r = n - 1;
            while (l < r) {
                int mid = l + r >> 1;
                if (arr[mid] >= k) {
                    r = mid;
                } else {
                    l = mid + 1;
                }
            }
            if (arr[l] != k) {
                System.out.println("-1 -1");
            } else {
                System.out.print(l + " ");
                l = 0;
                r = n - 1;
                while(l < r) {
                    int mid = l + r + 1 >> 1;
                    if (arr[mid] <= k) {
                        l = mid;
                    } else {
                        r = mid - 1;
                    }
                }
                System.out.println(l);
            }
        }
        br.close();
    }
}

02 数的三次方根

  • 题目

Snipaste_2023-01-16_20-22-16.png

  • 题解
import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        double n = Double.parseDouble(br.readLine());

        double l = -10000;
        double r = 10000;

        while (r - l > 1e-8) {    //注意这里题目要求保留6位小数,一般要多往后算两位保证精确
            double mid = (l + r) / 2;
            if (mid * mid * mid > n) {
                r = mid;
            } else {
                l = mid;
            }
        }
        System.out.printf("%.6f\n", l);
    }
}