9、二分查找

发布时间 2023-04-11 00:09:40作者: lidongdongdong~

1、二分查找法

我们这次实现一个比线性查找法快很多的算法,二分查找法,它的复杂度是 O(logN)

public class BinarySearch {

    private BinarySearch() {
    }

    /**
     * 二分查找法递归实现
     */
    public static <E extends Comparable<E>> int searchR(E[] data, E target) {
        return searchR(data, 0, data.length - 1, target);
    }

    private static <E extends Comparable<E>> int searchR(E[] data, int l, int r, E target) {
        if (l > r) return -1;

        int mid = l + (r - l) / 2;
        if (data[mid].compareTo(target) == 0) return mid;

        if (data[mid].compareTo(target) < 0) return searchR(data, mid + 1, r, target);
        return searchR(data, l, mid - 1, target);
    }

    /**
     * 二分查找法非递归实现
     */
    public static <E extends Comparable<E>> int search(E[] data, E target) {
        int l = 0;
        int r = data.length - 1;
        int mid;

        // 在 data[l, r] 中查找 target
        // 每次循环开始时: data[l]、data[r] 还没看, 因此 l == r 时, 依然要进入循环
        while (l <= r) {
            mid = l + (r - l) / 2;
            if (data[mid].compareTo(target) == 0) return mid;
            if (data[mid].compareTo(target) < 0) l = mid + 1;
            else r = mid - 1;
        }

        return -1;
    }
}

2、Ceil 上界

public class Ceil {

    private Ceil() {
    }

    /**
     * 在 data[] 中查找 > target 的最小值所在的索引
     */
    private static <E extends Comparable<E>> int upper(E[] data, E target) {
        int l = 0;
        int r = data.length;
        int mid;

        // 在 data[l, r] 中查找 > target 的最左边的索引, r = data.length
        // 每次循环开始时: data[l] 还没看, data[r] 可能是解, 因此当 l == r 时, r 就是解
        while (l < r) {
            mid = l + (r - l) / 2; // l <= mid < r, l 与 r 相邻时 mid = l
            if (data[mid].compareTo(target) > 0) r = mid;
            else l = mid + 1;
        }

        return r;
    }

    /**
     * <p>存在 target 时, 返回 = target 的最右边的索引
     * <p>没有 target 时, 返回 > target 的最左边的索引
     */
    public static <E extends Comparable<E>> int ceilR(E[] data, E target) {
        int upper = upper(data, target);
        if (upper - 1 >= 0 && data[upper - 1].compareTo(target) == 0) return upper - 1;
        return upper;
    }

    /**
     * <p>存在 target 时, 返回 = target 的最左边的索引
     * <p>没有 target 时, 返回 > target 的最左边的索引
     */
    public static <E extends Comparable<E>> int ceilL(E[] data, E target) {
        int l = 0;
        int r = data.length;
        int mid;

        // 在 data[l, r] 中查找 >= target 的最左边的索引, r = data.length
        // 每次循环开始时: data[l] 还没看, data[r] 可能是解, 因此当 l == r 时, r 就是解
        while (l < r) {
            mid = l + (r - l) / 2; // l <= mid < r, l 与 r 相邻时 mid = l
            if (data[mid].compareTo(target) >= 0) r = mid;
            else l = mid + 1;
        }

        return r;
    }
}

3、Floor 下界

public class Ceil {

    private Ceil() {
    }

    /**
     * 在 data[] 中查找 > target 的最小值所在的索引
     */
    private static <E extends Comparable<E>> int upper(E[] data, E target) {
        int l = 0;
        int r = data.length;
        int mid;

        // 在 data[l, r] 中查找 > target 的最左边的索引, r = data.length
        // 每次循环开始时: data[l] 还没看, data[r] 可能是解, 因此当 l == r 时, r 就是解
        while (l < r) {
            mid = l + (r - l) / 2; // l <= mid < r, l 与 r 相邻时 mid = l
            if (data[mid].compareTo(target) > 0) r = mid;
            else l = mid + 1;
        }

        return r;
    }

    /**
     * <p>存在 target 时, 返回 = target 的最右边的索引
     * <p>没有 target 时, 返回 > target 的最左边的索引
     */
    public static <E extends Comparable<E>> int ceilR(E[] data, E target) {
        int upper = upper(data, target);
        if (upper - 1 >= 0 && data[upper - 1].compareTo(target) == 0) return upper - 1;
        return upper;
    }

    /**
     * <p>存在 target 时, 返回 = target 的最左边的索引
     * <p>没有 target 时, 返回 > target 的最左边的索引
     */
    public static <E extends Comparable<E>> int ceilL(E[] data, E target) {
        int l = 0;
        int r = data.length;
        int mid;

        // 在 data[l, r] 中查找 >= target 的最左边的索引, r = data.length
        // 每次循环开始时: data[l] 还没看, data[r] 可能是解, 因此当 l == r 时, r 就是解
        while (l < r) {
            mid = l + (r - l) / 2; // l <= mid < r, l 与 r 相邻时 mid = l
            if (data[mid].compareTo(target) >= 0) r = mid;
            else l = mid + 1;
        }

        return r;
    }
}