(## 二分查找 ##
- 二分查找与一般查找相比,为什么要快?
- 二分查找的实现原理是什么?
- 二分查找的具体实现以及实例应用
- 二分查找的扩展(面向对象的程序设计)
1、二分查找与一般查找相比,为什么要快?
一般查找算法(从头到尾依次比较)
public static int rank(int key , int[] arr){
for(int index = 0 ; index < arr.length ; index++){
if(arr[index] == key){
return index;
}
return -1;
}
}
这种暴力实现处理大量输入非常慢。
然而,二分查找之所以快,是因为它只需要检查很少几个条目(相较于数组的大小来说),就可以找到目标元素(或者确认目标元素不存在)。
2、二分查找的实现原理是什么?
原理图
3、二分查找的具体实现以及实例应用
开发用例:白名单过滤
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Scanner;
public class BinarySearch {
/**
* 二分查找
*
* @param key 要查找的整数键
* @param arr 数组必须是有序的
* @return 如果该整数键存在于数组中,则返回它的索引,否则返回-1
*/
public static int rank(int key, int[] arr) {
int lo = 0;
int hi = arr.length - 1;
while (lo <= hi) {
int mid = lo + ((hi - lo) >> 1);//①:a>>n相当于a/2^n。②:此处有坑,不要图快用加法,会溢出。即:mid = (lo + hi) >> 1;③、注意 >> 的优先级别低于 + ,也就是说先执行 + ,在执行 >>
if (key < arr[mid]) {
hi = mid - 1;
} else if (key > arr[mid]) {
lo = mid + 1;
} else {
return mid;
}
}
return -1;
}
/**
* 开发用例:白名单过滤
* 过滤掉标准输入中的所有存在于白名单中的条目,仅将不在白名单上的整数打印到标准输出中
* @param args
*/
public static void main(String[] args) {
int key;
int[] whiteList = {10,77,12,16,98,23,48,57,29,54,33,68,11,84,18};
Arrays.sort(whiteList);
Scanner in = new Scanner(new BufferedReader(new InputStreamReader(System.in)));
while(in.hasNext()){
key = in.nextInt();
if(rank(key,whiteList) < 0){
System.out.println(key);
}
}
}
}
4、二分查找的扩展(面向对象的程序设计)
一般来说,算法是某个抽象数据类型的一个实例方法的实现。正如前面白名单的例子就被实现为一个抽象数据类型的用例。它进行了以下操作:
- 由一组给定的值构造了一个SET(集合)对象;
- 判定一个给定的值是否存在于该集合中。
这些操作封装在 StaticSETofInts 抽象数据类型中。
将上面二分查找的代码重写为一段面向对象的程序(用于在整数集合中进行查找的一种抽象数据类型
典型的用例
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Scanner;
/**
* 典型用例
* @author TinyDolphin
* 2017/5/7 17:13.
*/
public class WhiteList {
public static void main(String[] args) {
int[] whiteList = {10,77,12,16,98,23,48,57,29,54,33,68,11,84,18};
StaticSETofInts staticSETofInts = new StaticSETofInts(whiteList);
Scanner in = new Scanner(new BufferedReader(new InputStreamReader(System.in)));
while(in.hasNext()){
int key = in.nextInt();
//如果key,不在白名单中,则打印它
if(!staticSETofInts.contains(key)){
System.out.println(key);
}
}
}
}
数据类型的实现
import java.util.Arrays;
/**
* 二分查找--数据类型的实现
* @author TinyDolphin
* 2017/5/7 16:37.
*/
public class StaticSETofInts {
/**
* 类中内置数组
*/
private int[] arr;
/**
* 数组的大小
*/
private int size;
public StaticSETofInts(int[] keys) {
size = keys.length;
arr = new int[size];
System.arraycopy(keys, 0, arr, 0, size); //保护性复制(保护原数组的顺序),此处使用了性能较优的数组复制方法
Arrays.sort(arr);
}
/**
* 该key是否存在于数组中
*
* @param key 查找的key
* @return 若存在,返回true,否则返回false
*/
public boolean contains(int key) {
return rank(key) != -1;
}
/**
* @param key 查找的key
* @return key所在的位置
*/
private int rank(int key) {
int lo = 0;
int hi = size - 1;
while (lo <= hi) {
int mid = lo + (hi - lo) >> 1;//①:a>>n相当于a/2^n。②:此处有坑,不要图快用加法,会溢出。即:mid = (lo + hi) >> 1;
if (key < arr[mid]) {
hi = mid - 1;
} else if (key > arr[mid]) {
lo = mid + 1;
} else {
return mid;
}
}
return -1;
}
}
使用 Java 的类机制来支持数据的抽象将使我们受益匪浅。