Jdk之中的二分查找法
闲逛看代码,看到了JDK之中实现的二分查找法。这里做一下介绍:
直接看源码
/**
* not sorted, the results are undefined. If the list contains multiple
* elements equal to the specified object, there is no guarantee which one
* will be found.
* 在指定的list中查找指定的对象,二分查找法。这个list必须根据指定的排序规则comparator排序好。
* 如果这个list没排序,返回的结果是不确定的。
* 如果是list实现了 RandomAccess 接口,或者长度小于5000,那么它的查找时间复杂度是 log(n),
* 如果list没实现,并且它的大小大于5000,它的时间复杂度就是O(logn)
*/
@SuppressWarnings("unchecked")
public static <T> int binarySearch(List extends T> list, T key, Comparator super T> c) {
if (c==null)
return binarySearch((List extends Comparable super T>>) list, key);
if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
//BINARYSEARCH_THRESHOLD = 5000
return Collections.indexedBinarySearch(list, key, c);
else
return Collections.iteratorBinarySearch(list, key, c);
}
上面的代码一眼看上去挺奇怪的,为什么还有两个实现,二着有什么区别:
两段代码都特别简单:
// 二分查找算法
private static <T> int indexedBinarySearch(List<? extends T> l, T key, Comparator<? super T> c) {
int low = 0;
int high = l.size()-1;
while (low <= high) {
int mid = (low + high) >>> 1; //表示除以2
T midVal = l.get(mid);//和 iteratorBinarySearch 区别在这里
int cmp = c.compare(midVal, key); // 比较
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1); // key not found
}
private static <T> int iteratorBinarySearch(List<? extends T> l, T key, Comparator<? super T> c) {
int low = 0;
int high = l.size()-1;
ListIterator<? extends T> i = l.listIterator();
while (low <= high) {
int mid = (low + high) >>> 1;
T midVal = get(i, mid); // 和 indexedBinarySearch 的 差别在这里,这里需要while循环找到指定位置的节点。所以时间复杂度高。
int cmp = c.compare(midVal, key);
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1); // key not found
}
private static <T> T get(ListIterator<? extends T> i, int index) {
T obj = null;
int pos = i.nextIndex();
if (pos <= index) {
do {
obj = i.next();
} while (pos++ < index);
} else {
do {
obj = i.previous();
} while (--pos > index);
}
return obj;
}
从这里可以不难看出indexedBinarySearch 的效率为什么比 iteratorBinarySearch高,因为 iteratorBinarySearch是通过loop的方式找到对应的元素,而indexedBinarySearch可以通过下标直接访问到元素。一个复杂度是O(logn),一个是O(1)。
然后进入两个的区别是 list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD。这个表示list实现了 RandomAccess
接口,或者元素少于 5000。
我们重点看看 RandomAccess
。发现它其实是一个空interface。
public interface RandomAccess {
}
大概的意思就是实现了RandomAccess
就可以使用类似list.get(i) 获取到元素,不然就要使用iterator的方式loop找到这个元素。又学到一样东西。
另外Collections还有几个类似的方法:
public static <T> int binarySearch(List extends T> list, T key, Comparator super T> c) {
if (c==null)
return binarySearch((List extends Comparable super T>>) list, key);
if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
return Collections.indexedBinarySearch(list, key, c);
else
return Collections.iteratorBinarySearch(list, key, c);
}
// 二分查找算法
private static <T> int indexedBinarySearch(List extends T> l, T key, Comparator super T> c) {
int low = 0;
int high = l.size()-1;
while (low <= high) {
int mid = (low + high) >>> 1; //表示除以2
T midVal = l.get(mid);//和 iteratorBinarySearch 区别在这里
int cmp = c.compare(midVal, key); // 比较
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1); // key not found
}
private static <T> int iteratorBinarySearch(List extends T> l, T key, Comparator super T> c) {
int low = 0;
int high = l.size()-1;
ListIterator extends T> i = l.listIterator();
while (low <= high) {
int mid = (low + high) >>> 1;
T midVal = get(i, mid); //和indexedBinarySearch区别在这里
int cmp = c.compare(midVal, key);
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1); // key not found
}
关于我的jdk+jvm包的源码学习,带注解的在下面的链接都有:
https://gitee.com/ydonghao/openjdk11.git
https://gitee.com/ydonghao/openjdk14.git