文章目录
想法来源
\quad\quad
lower_bound
与upper_bound
都是C++
的<algorithm>
头文件提供的两个函数,用于分别查找大于等于以及严格大于给定值的最小下标,具体图示可见hacking cpp网站。
\quad\quad
而在使用Rust
刷题的过程中,我经常使用partition_point
来完成类似的功能,而点开partition_point
的源码实现,发现其底层基于二分查找实现。
\quad\quad
于是产生了一个想法:既然Java
提供了binarySearch
函数,那么是否可以遵循类似的思路,从binarySearch
函数中构造一个lowerBound
函数以及upperBound
函数呢?
代码实现
实现思路
\quad\quad
重点在于修改binarySearch
的自定义Comparator
。binarySearch
会利用Comparator
比较midValue
与给定的key
的大小,从而决定二分过程中左右边界的移动。而我们所需要做的就是根据upper_bound
与lower_bound
的函数逻辑,重写这个Comparator
。
\quad\quad
由于重写的Comparator
并没有给出相等逻辑(其实对于upper_bound
根本给不出相等逻辑判定,因为很难知道下一个严格大于给定数字的数值到底是多大),所以binarySearch
给出的其实是所谓的插入坐标,是一个负数,因此最后需要转换一下这个插入坐标。
lowerBound
实现
private static <T extends Comparable> int lowerBound(T[] arr, T key) {
int index = Arrays.binarySearch(arr, key, new Comparator<T>() {
final int LESS = -1;
final int GREATER = 1;
@Override
public int compare(T midValue, T key) {
if (midValue.compareTo(key) < 0) {
return LESS;
} else {
return GREATER;
}
}
});
return -(index+1);
}
upperBound
实现
private static <T extends Comparable> int upperBound(T[] arr, T key) {
int index = Arrays.binarySearch(arr, key, new Comparator<T>() {
final int LESS = -1;
final int GREATER = 1;
@Override
public int compare(T midValue, T key) {
if (midValue.compareTo(key) <= 0) {
return LESS;
} else {
return GREATER;
}
}
});
return -(index+1);
}
partitionPoint
实现
private static <T> int partitionPoint(T[] arr, Predicate pred) {
int index = Arrays.binarySearch(arr, 0, (midValue, dummy) -> {
if (pred.test(midValue)) {
return -1;
} else {
return 1;
}});
return -(index+1);
}
\quad\quad
整体逻辑已经与rust
中的partition_point
函数较为接近了。不过binarySearch
的key
形参即待查找对象已经没有用了,顺带重写Comparator
逻辑中,也只关心midValue
,不再关心查找对象key
。
调用结果
lowerBound
与upperBound
调用
public class Main {
public static void main(String[] args) {
Integer[] arr = {1, 2, 3, 3, 3, 4, 5, 6};
int key = 3;
int i = upperBound(arr, key);
System.out.println("Upper bound index is " + i);
i = lowerBound(arr, key);
System.out.println("Lower bound index is " + i);
}
}
Upper bound index is 5 # 即`arr[5]` = 4, 第一个严格大于3的数字下标
Lower bound index is 2 # 即`arr[2]` = 3, 第一个大于等于3的数字下标
partitionPoint
调用
public class Main {
public static void main(String[] args) {
Integer[] v = {1, 2, 3, 4, 5, 6, 7};
int i = partitionPoint(v, x -> (Integer) x < 5);
System.out.println("Returns the index of the partition point according to the given predicate (the index of the first element of the second partition):");
System.out.println(i);
i = partitionPoint(v, x -> (Integer) x < 100);
System.out.println("If all elements of the slice match the predicate, including if the slice is empty, then the length of the slice will be returned:");
System.out.println(i);
}
}
Returns the index of the partition point according to the given predicate (the index of the first element of the second partition):
4
If all elements of the slice match the predicate, including if the slice is empty, then the length of the slice will be returned:
7
\quad\quad
整个调用例与Rust std docs中的例子一致,结果也相符。
最后总结
\quad\quad 声明:
本程序纯粹是出于兴趣的“造轮子”行为,并不承诺以上算法具有很好的鲁棒性和较高的性能表现!不喜勿喷。