要求: 從數組中找出指定的關鍵值(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; }