斐波那契查找算法詳解
說明
- 斐波那契查找算法核心思想類似於二分查找和插值查找,區別在於對標志值,即 mid 的設計算法不一樣,二分查找直接重用中間值作為標桿,插值查找使用自適應確定mid,而斐波那契查找算法則使用黃金分割,使得mid總是處於查找數列的黃金分割點位置
- 因為斐波那契數列越到后邊,相鄰兩數的比值越發接近0.618,也就是黃金分割比,因為可以巧妙的使用斐波那契數列尋找數組中的黃金分割點,即mid對應的下標
- 因此需要先構建一個斐波那契數列,可以使用遞歸的方法或者非遞歸的方式
- 使用斐波那契數列尋找數組的黃金分割點公式為: mid = low + f (k - 1) - 1,k為當前斐波那契數對應的索引
- 使用斐波那契數列查找,需要先將當前數組的長度構建為第一個比數組長度大的斐波那契數,這個數對應的索引就是 k ,可以使用循環的方法
- 將構建的新數組后邊補零的位置替換為數組中的最后一個位置,即最大值
- 准備工作准備好后,就可以計算當前數組的黃金分割值,然后獲取到當前黃金分割值對應的元素
- 將這個元素和要查找的元素進行比較,然后重置左右指針和重置后數組對應的黃金分割點
- 當查找完所有的元素后,如果沒有找到,則返回 - 1
- 注意斐波那契數列的特性 即 當索引 > 2時,當前位置元素 = 前兩個位置元素之和,而前兩個位置元素之比剛好是滿足黃金分割,正是基於這樣的特性,才有公式 mid = low + f (k - 1) - 1
- 斐波那契查找算法不易理解,須慢慢體會
- 源碼及詳解見下
源碼及分析
//斐波那契數列的最大長度
public static int maxSize = 20;
public static void main(String[] args) {
int[] arr = {1, 23, 45, 66, 67, 88, 90, 100};
int index = fisSearch(arr, 88);
System.out.println("index = " +index);
}
//構建斐波那契數列
public static int[] fis() {
int[] f = new int[maxSize];
f[0] = 1;
f[1] = 1;
for (int i = 2; i < f.length; i++) {
f[i] = f[i - 1] + f[i - 2];
}
return f;
}
/**
* 斐波那契查找算法實現
*
* @param arr 要查找的原始數組
* @param key 要查找的值
* @return 查找的結果
*/
public static int fisSearch(int[] arr, int key) {
//數組左側索引
int low = 0;
//數組右側索引
int high = arr.length - 1;
//比右側索引大的第一個斐波那契數對應的索引
int k = 0;
//黃金分割點
int mid = 0;
//斐波那契數列
int[] f = fis();
//由數組最大值計算k
while (high > f[k] - 1) {
k++;
}
//因為f[k]的值可能大於數組的長度,因此需要給原數組擴容到長度 == f(k)
int[] tmp = Arrays.copyOf(arr, f[k]);
//調用copyOf方法后在擴容部分全部補了0,實際上需要補數組的最后一位
for (int i = high + 1; i < tmp.length; i++) {
tmp[i] = arr[high];
}
//使用while循環來查找需要找的數
while (low <= high) {
//先計算黃金分割點
mid = low + f[k - 1] - 1;
//判斷黃金分割點的元素和要查找的元素的關系
//如果要查找的值在mid左邊,重置high和k
if (tmp[mid] > key){
high = mid - 1;
k--;
//如果要查找的值在mid右邊
}else if (tmp[mid] < key){
low = mid + 1;
k -= 2;
//否則找到該元素
}else {
if (mid <= high){
return mid;
}else {
return high;
}
}
}
//如果循環結束后還沒有找到,說明沒有
return -1;
}