要求: 从数组中找出指定的关键值(key),经常使用的查找算法有4种:
(1)线性查找,也称为顺序查找
(2)二分查找
(3)插值查找
(4)斐波那契查找
说明: (2)、(3)、(4)本质上都是经过数组的中间值,将关键字(key)逐渐缩小查找范围,二分查找以数组中间值将查找范围缩小一半;插值查找也是经过某个中间值来缩小查找范围,可是中间值是动态变化的,根据插值公式计算;同理斐波那契查找算法是经过斐波那契数列关系推导出中间值,从而缩小关键值的查找范围。java
遍历数组,逐个比较,适用于小规模数据的查找,查找效率慢!
要求: 任意数组
Java代码实现算法
public int seqSearch(int[] array,int key){ for(int i=0;i<array.length;i++){ if(array[i]==key){ return i; } } return -1; }
二分查找经过有序数组的中间元素将数组分红两部分,每次查找过程当中将关键值key锁定在一部分中,从而缩小了一半的查找空间。
要求: 有序数组数组
public int binarySearch(int[] array,int left,int right,int key){ //递归的终止条件 if(left>right){ return -1; } int mid=(left+right)/2; if(key<array[mid]){ return binarySearch(array,left,mid-1); }else if(key>array[mid]{ return binarySearch(array,mid+1,right); }else{ return mid; } } //非递归版本 public int binarySearch2(int[] array,int left,int right,int key){ while(left<=right){ int mid=(left+right)/2; if(key<array[mid]){ //key在[left,mid-1]; right=mid-1; }else if(key>array[mid]{ //key在[mid+1,right] left=mid+1; }else{ return mid; } } }
本质上和二分查找同样,只是再也不固定中间元素mid,mid的选取是动态变化,根据插值函数变化:\(mid=low+\frac{key-array[low]}{array[high]-array[low]}(high-low)\)
注意: 使用该公式时,须要保证:array[0]<=key<=array[array.length-1];不然可能会产生索引越界异常;
要求: 有序数组
java代码缓存
public int insertSearch(int[] array,int left,int right,int key){ if(left>right || key<array[left] || key>array[right]){ return -1; } int=mid=left+((key-array[left])/(array[right]-array[left]))*(right-left); if(key<array[mid]){ return insertSearch(array,left,mid-1); }else if(key>array[mid]{ return insertSearch(array,mid+1,right); }else{ return mid; } }
需求: 不只要找到关键值,当数组中有多个关键值的时候,求出他们的索引位置?
思路分析:
(1)当找到一个关键值索引时候,不要当即返回,应该分别从该索引处向左、向右分别寻找,直到全部的关键值都找到为止,能够将中间值缓存在一个ArrayList中,当查询全部完成之后一并返回便可;
java代码函数
public List<Integer> insertSearch(int[] array,int left,int right,int key){ if(left>right || key<array[left] || key>array[right]){ return new ArrayList(); } int=mid=left+((key-array[left])/(array[right]-array[left]))*(right-left); if(key<array[mid]){ return insertSearch(array,left,mid-1); }else if(key>array[mid]{ return insertSearch(array,mid+1,right); }else{ //找到了; ArrayList<Integer> resList=new ArrayList<Integer>(); //向当前值的左边寻找 int tmp=mid-1; while(tmp>=0 && array[tmp]==key){ resList.add(tmp); tmp--; } //将当前值索引添加进入resList resList.add(mid); //向当前值的右边寻找 int tmp=mid+1; while(tmp<=array.length-1 && array[tmp]==key){ resList.add(tmp); tmp++; } return resList;//此时返回并结束 } }
(1)黄金分割点。黄金分割点将一条线段分红两部分,使得一部分线段长度与整条线段长度的比值等于另外一部分线段长度与该部分线段长度的比值,比值为0.618
(2)斐波那契数列{1,1,2,3,5,8,13,21,35,},前两个数之和为第三个数,而且相邻两个数之比趋近于黄金分割点。
原理: 斐波那契查找算法,再也不经过插值公式肯定中间点mid,而是经过斐波那契数数列关系来得到mid,这也是一个动态获取的过程,那斐波那契数列与mid究竟有怎样的关系呢?或者怎样让他们发生关系呢???
废话很少说,先上图:
mid=low+F[k-1]-1
简单推导过程
(1)因为斐波那契数知足:\(F[k]=F[k-1]+F[k-2],k>=2\);
(2)等式两边同时-1,即:\(F[k]-1=(F[k-1]-1)+(F[k-2]-1)+1,k>=2\),如上图所示:一个数组长度为F[k]-1的数组array,能够划分为三个部分:[low,F[k-1]-1-1],[mid],[mid+1,F[k-2]-1-1];即:mid=low+F[k-1]-1
有了上面的公式,咱们想要获得mid,首先须要获得斐波纳契数表达式F[k],根据上图能够知道:array.length-1=F[k]-1;而F[k]属于离散型的数列,不必定能保证其值刚好等于array.length-1,可是咱们必要要保证F[k]-1>=arry.length便可符合要求,经过程序很他容易求得k的值。
要求: 有序数组spa
//获取符合斐波那契函数特征的数组,并指定数组的最大个数 public static int[] fibonacci(int maxSize){ int[] fib=new int[maxSize]; fib[0]=1; fib[1]=1; for(int i=2;i<maxSize;i++){ fib[i]=fib[i-1]+fib[i-2]; } return fib; } public int fibonacciSearch(int[] array,int key){ int low=0; int high=array.length-1; int k=0; int[] fib=fibonacci(array.length); while(fib[k]-1<high){ k++ } //此时k的取值恰好使得fib[k]-1>=high, //fib[]多余的部分使用array数组的最高位来填充 int[] tmp=Arrays.copyOf(array,fib[k]) for(int i=high+1;i<tmp.length;i++){ //tmp的多余部分使用Array.copyOf()函数是默认填充0,咱们须要填充arry的最高位 tmp[i]=array[high]; } //根据斐波那契公式求mid,k已经知道 while(low<=high){ int mid=low+fib[k-1]-1//由上述推导公式可得 //二分查找开始 if(key<array[mid]){ //key在[low,mid-1] high=mid-1; //继续根据fib[]查找[low,mid-1]部分的mid k=k-1; //为何是k=k-1呢? //说明: //fib[k]-1=(fib[k-1]-1)+1+(fib[k-2]-1) //因为fib[k-1]=fib[k-2]+fib[k-3]即: //(fib[k-1]-1)=(fib[k-2]-1)+1+(fib[k-3]-1);k由k-1到k-2;执行k--便可 }else(key<array[mid]){ //key在[mid+1,high] low=mid+1; k=k-2; //为何-2,原理同上,k-1到k-3中间须要执行k-2的操做 }else{ //找到,就是mid return mid; } } return -1; }