定义
查找就是在某个项目群中,找到其中想要的项目,或者说确定想要的项目是否在该项目群中。
线性查找
从头到尾一次比较每一个值,知道找到目标值。
public static <T extends Comparable<? super T>> boolean linearSearch(
T[] data, int min, int max, T target){
int index = min;
boolean found = false;
while(!found && index <= max){
if(data[index].compareTo(target) == 0){
found = true;
}
index++;
}
return found;
}
上面这个方法就是用来做线性查找的静态的泛型方法。这段代码和明确主要逻辑就在data[index].compareTo(targer) == 0
campareTo相等就是0,这时候改变逻辑条件,跳出循环返回即可。
T extend Comparable<T>
这句话的意思就是,有一个类型T实现了了Comparable接口,该接口类型为T。因为我们要查找,就需要比较所以必须要实现这个接口才可以。
T extend Comparable<? super T>
这句话的意思就是,有一个类型T实现了了Comparable接口,该接口类型为T或者是T父类。
静态方法
静态方法又叫做类方法,在JVM虚拟机中,它存在于被共享区域,关于虚拟机的学习,请关注后续相关文章。
泛型方法
这是在JDK1.5之后的一种新的定义,主要用于集合等的类型限制,这样也避免了我们在使用集合的时候出现各种不可预估的类型转换错误等。
泛型声明必须要在返回类型之前,这与泛型才可以作为返回类型的一部分。
二分查找法
如果查找池已排序,那么使用二分查找的效率会比线性查找高出很多。
二分查找顾名思义就是从中间开始,如果带查找在A分群,那抛弃B,继续二分A分群,直到没有目标或者说找到目标。
public static <T extends Comparable<? super T>> boolean binarySearch(
T[] data, int min, int max, T target){
// 索引从中间节点开始
int midpoint = (min + max) / 2;
boolean found = false;
if(data[midpoint].compareTo(target) == 0){ // 找到
found = true;
}else if(data[midpoint].compareTo(target) > 0){// 在当前部分,当前中间节点和目标比大于0表示目标值在中间节点之前
if(min < midpoint - 1){// 判断是否还能再进行二分,递归查找
found = binarySearch(data, min, midpoint-1, target);
}
}else if(midpoint + 1<= max){// 在另一部分了,因为已经有==0 和 > 0 这里就省略了 < 0 而直接判断是否这一部分还能二分
found = binarySearch(data, min, midpoint+1, target);
}
return found;
}
在二分的过程中,很可能会出现偶数个带查找值,也就是二分之后有两个中间值,这时候程序会丢弃小数部分,选择中间值的第一个。
递归
递归的意思就是调用自身,如果说我们再进入递归之前没有条件,或者说这个条件可能永远满足,那么就会造成死循环和内存溢出,因此我们再使用递归时需要谨慎。
查找算法的比较
线性比较分析
从上面两段代码来看,线性查找最理想的就是带查找内容在项目群的第一位,这样我们耗时最小,但在实际应用中,这种可能性几乎为零,最不理想的状态就是在最后一位。在之前的博客数据结构-算法分析中说过,算法的时间复杂度分析要抛开常量的影响,虽然比较次数会影响到我们的具体时间,但是不会影响我们说的复杂度。
二分比较分析
这里我们就不再考虑最理想的状态,常规应用中,假设我们又20个数字需要比较,在线性中最多20次比较,就能得到结果。在二分中,第一次后剩10,第二次后5,第三层3,第四次或者第五次出结果。也就是说二分比较的效率比线性高太多,如果20换成200W,可自行尝试。由此可见二分的时间复杂度为对数级别 O ( l o g 2 n ) O(log_2n) O(log2n),但是二分需要先排序。