对于查找算法,耳熟能详的就是二分查找了。对于查找算法来说,由于在实际应用中会把数据以一定的数据结构(如二叉树,红黑树等)组织起来,很少会直接使用一个数组或链表对数据直接存储。因此,对数据的查找算法没有排序算法那样多样性,而是都包含在各个数据结构中了。这里的查找算法,仅针对有序的数组的查找。
(参考:https://www.cnblogs.com/yw09041432/p/5908444.html
https://blog.csdn.net/luochoudan/article/details/51629338)
1、顺序查找
就是对无序数组进行查找,把数据一个个与给定的关键字作比较。这没什么好说的。时间复杂度为O(n)。
2、二分查找
元素必须是有序的。最坏情况下,时间复杂度为O(log2n)(2为底,下同)。当然最好情况下,一次就能找到了。当然代码也是相当简单。分别有递归版和非递归版。当然,这里可以为了提升效率,做个小小的改进。除以2,可以用移位计算(high-low)>>2,因为移位计算比除法快。
public class Main{
public static void main(String[] args){//二分查找非递归版
int[] a=new int[] {1,2,3,4,5,6,7,8,9,10};
int low=0;
int high=9;
int key=1;
while(low<=high) {
int mid=low+(high-low)/2;
if(a[mid]==key) {
System.out.println(mid);
break;
}
else if(a[mid]>key) {
high=mid-1;
}
else {
low=mid+1;
}
}
}
}
public class Main{
public static void main(String[] args){//二分查找递归版
int[] a=new int[] {0,1,2,3,4,5,6,7,8,9};
System.out.println(binarysearch(a, 3, 0, 9));
}
public static int binarysearch(int[] a,int key, int low, int high) {
int mid=low+(high-low)/2;
if(a[mid]==key)
return mid;
else if(a[mid]>key) {
return binarysearch(a, key, low, mid-1);
}
else {
return binarysearch(a, key, mid+1, high);
}
}
}
3、插值查找
其实插值查找是对二分查找的一种改进。对于二分查找来说,为什么每次都只是折半查找呢,这样就非常不灵活,如果有什么办法,根据关键字的值,可以把每次查找的空间做一个自适应的改进,增大或缩小区间,这样就更好了,因此有了插值查找。插值查找其实改进点就是,把查找的点改为:
mid=low+(key-a[low])/(a[high]-a[low])*(high-low)
根据关键字在整个有序表中所处的位置,让mid值的变化更靠近关键字key,这样也就间接地减少了比较次数。对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好的多。反之,数组中如果分布非常不均匀,那么插值查找未必是很合适的选择。查找成功或者失败的时间复杂度均为O(log2(log2n))。4、斐波那契查找
黄金比例又称黄金分割,是指事物各部分间一定的数学比例关系,即将整体一分为二,较大部分与较小部分之比等于整体与较大部分之比,其比值约为1:0.618或1.618:1。对于斐波那契数列:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89…….(从第三个数开始,后边每一个数都是前两个数的和)。然后我们会发现,随着斐波那契数列的递增,前后两个数的比值会越来越接近0.618,利用这个特性,我们就可以将黄金比例运用到查找技术中。
斐波那契查找也是对二分查找的一种改进。这种查找的精髓在于采用最接近查找长度的斐波那契数值来确定拆分点。假设有一个待查找的数组a[n],有n个元素,和斐波那契数列F,并且满足n>=F[k]-1&&n<F[k+1]-1,那可以认为他的第一个分割点mid=F[k]-1。由于斐波那契数列中,F[k]=F[k-1]+F[k-2],因此,最完美的情况下,n=F[k]-1,则数组a[a[0],a[1],a[2],...,a[n-1]]被分割成:
[a[0](a[low]),a[1],...,a[mid],a[mid+1],...,a[n-1](a[high])]。
a[mid]前面有F[k-1]-1个元素,后面又F[k-2]-1个元素,一共有F[k]-1个元素。但是事实上很难n刚好等于F[k]-1,因此后面部分一般会有残缺。因此一般用最大的数来补全后面残缺的部分。如果查找的位置落到补齐的部分,那就可以确定要找的那个数就是最后一个最大的了。
其实个人觉得斐波那契查找并没有太大用处,而且还要消耗额外空间存储斐波那契数列。而且对于这种线性表,频繁的插入删除操作开销很大。还不如用树来组织数据。