BinarySearch的一些变种

upper lower ceil floor

Posted by Xiaohan on December 28, 2021

今天学习了一些BinarySearch的一些变种,方便在浮点数取正运算。但是对于一些名称的定义有些疑惑,查了一些资料没找到想要的答案,特意贴到这里供大家讨论

代码

upper

在数组data中寻找大于target值的最小值的index

在合格的商品中找到最低品质

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// > target 的最小值
    // X X X [target] res X X
    // X X X [target-0.01] res X X
    public static <E extends Comparable<E>> int upper(E[] data, E target) {
        int l = 0, r = data.length;
        while (l < r) {
            int mid = l + (r - l) / 2;
            if (data[mid].compareTo(target) <= 0) {
                //data[0,mid]都小于等于target
                //有可能data[mid] == target; l = r = mid + 1;
                l = mid + 1;
            } else r = mid;
        }
        return l;
    }

lower

在数组data中寻找小于target值的最大值的index

在不及格的学生中找到成绩最好的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// < target 的最大索引值
    // X X X res [target] X X
    // X X X res [target+0.01] X X
    public static <E extends Comparable<E>> int lower(E[] data, E target) {
        int l = -1, r = data.length - 1;
        while (l < r) {
            int mid = l + (r - l) / 2;
            if (data[mid].compareTo(target) < 0) {
                //data[mid,data.length-1]都大于等于target
                //有可能data[mid] == target; l = r = mid - 1;
                l = mid;
            } else r = mid - 1;
        }
        return l;
    }

upper_ceil

在数组data中寻找大于等于target值的最小索引

如果target存在,返回重复target的最大索引,如果target的不存在则返回upper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 存在target返回最大的索引
    // 不存在target返回upper(刚刚超过target的数字)
    // 上1法(五入法)
    //X X X [target] [target] [target_res] X X
    //X X X [target-0.01] [target-0.01] [target+0.01_res] X X
    //原名为ceil和lower_floor呼应(最顶上或者超过)
    public static <E extends Comparable<E>> int upper_ceil(E[] data, E target) {
        int u = upper(data, target);
        if (u - 1 >= 0 && data[u - 1].compareTo(target) == 0) {
            //如果upper左边的值和target一样,存在,u-1就是最大索引
            return u - 1;
            //如果不想等,则不存在,返回upper
        } else return u;
    }

lower_floor

在数组data中寻找小于于等于target值的最大索引

如果target存在,返回重复target的最小索引,如果target的不存在则返回lower

1
2
3
4
5
6
7
8
9
10
11
12
// 存在target返回最小的索引
    // 不存在target返回lower(刚刚超过target的数字)
    // X X X [target_res] [target] [target] X X
    // X X [target-0.01_res] [target+0.01] [target+0.01] X X
    // 与upper_ceil相呼应,最低或者小于
    public static <E extends Comparable<E>> int lower_floor(E[] data, E target) {
        int l = lower(data, target);
        if (l + 1 < data.length && data[l + 1].compareTo(target) == 0) {
            return l + 1;
        }
        return l;
    }

疑问

从上述的upper,lower,floor,ceil的实现方式来看,可以这样理解:

floor是所有target的最小索引,lower在floor向下一位

ceil是所有target的最大索引,upper在ceil向上一位

如果按照字面意思,把target值当作一个房子,那么floor为地板,即target最小索引,ceil为天花板,target最大索引,而upper和lower则分别代表了屋顶以上,和地板以下。

而对于两位两种组合upper_floor 和 lower_ceil 的定义和这个就完全相悖了

upper_floor

target存在返回最大索引,不存在返回lower

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    // 存在target返回最大的索引
    // 不存在target返回lower(刚刚超过target的数字)
    // X X X [target] [target] [target_res] X X
    // X X X res [target+0.01] [target+0.01]  X X
    // 最高或者小于 (叫做lower_ceil更合适呢)
    public static <E extends Comparable<E>> int upper_floor(E[] data, E target) {
        int l = -1, r = data.length - 1;

        while (l < r) {
            int mid = l + (r - l) / 2;
            if (data[mid].compareTo(target) <= 0) {
                //data[mid,r]都小于等于target,不断向右边界逼近。
                //如果存在target则是在所有target值中的最右边
                //如果不存在则是小于target的右边界
                l = mid;
            } else r = mid - 1;
        }
        return l;
    }

lower_ceil

target存在返回最小索引,不存在返回upper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 存在target返回最小的索引
    // 不存在target返回upper(刚刚超过target的数字)
    // X X X [target_res] [target] [target] X X
    // X X X [target-0.01] [target-0.01] res X X
    // 最低或者超过(应该叫做upeer_floor呢)
    public static <E extends Comparable<E>> int lower_ceil(E[] data, E target) {
        int l = 0, r = data.length;
        while (l < r) {
            int mid = l + (r - l) / 2;
            if (target.compareTo(data[mid]) > 0) {
                //只有在大于的时候才往右移动,如果相等,也是向左移动(比如5,5,5,5,5中查找5,无论是哪个五都会向左移动
                //直到移到5的左边,然后向右移动一位,则就是第一个5,最小的索引
                l = mid + 1;
            } else r = mid;
        }
        return l;
    }

奇怪

参考链接

https://its201.com/article/u013250861/109302494 https://blog.csdn.net/woaichikaoya/article/details/115742841

欢迎大家来讨论!!