【数据结构】斐波那锲查找算法
在写斐波那锲查找算法时遇到ArrayIndexOutOfBoundsException错误,错误提示如下图,即出现了k=0,程序查询第k-1个斐波那锲数列的值时出现了索引越界。
下面是修改错误后运行正确的斐波那锲查找算法
import java.util.*;
/**
* @author Calculus
* @create 2022-04-21 20:22
*/
public class FibonacciSearch {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 39, 39, 39, 100, 100, 100, 1001};
int[] fibArray = getFib(arr);
int[] newArr = extendArr(arr, fibArray);
//修改错误的关键所在!
fibArray[0] = 0;
Set<Integer> set = search(newArr, arr.length, 0, newArr.length - 1, 0, fibArray, fibArray.length - 1);
System.out.println(set);
}
// 获取斐波那锲数列
public static int[] getFib(int[] arr) {
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(1);
while (true) {
if (list.get(list.size() - 1) >= arr.length) {
break;
}
list.add(list.get(list.size() - 1) + list.get(list.size() - 2));
}
int[] fibArray = list.stream().mapToInt(Integer::intValue).toArray();
return fibArray;
}
// 延长arr数组
public static int[] extendArr(int[] arr, int[] fibArray) {
// 延长arr数组
int[] newArr = Arrays.copyOf(arr, fibArray[fibArray.length - 1]);
if (arr.length < newArr.length) {
for (int i = arr.length; i < newArr.length; i++) {
newArr[i] = arr[arr.length - 1];
}
}
return newArr;
}
// 查找
public static Set<Integer> search(int[] newArr, int arrLength, int left, int right, int targetValue, int[] fibArray, int k) {
if (left > right) {
return new HashSet<Integer>();
}
// 取mid数值
int mid = left + fibArray[k - 1] ;
// 寻找
if (newArr[mid] < targetValue) {
k -= 2;
return search(newArr, arrLength, mid + 1, right, targetValue, fibArray, k);
} else if (newArr[mid] > targetValue) {
k -= 1;
return search(newArr, arrLength, left, mid - 1, targetValue, fibArray, k);
} else {
Set<Integer> set = new HashSet<>();
if (mid > arrLength - 1) {
set.add(arrLength - 1);
} else {
set.add(mid);
}
int temp = mid - 1;
while (temp >= 0 && newArr[temp] == newArr[mid]) {
if (temp > arrLength - 1) {
set.add(arrLength - 1);
} else {
set.add(temp);
}
temp--;
}
temp = mid + 1;
while (temp < newArr.length && newArr[temp] == newArr[mid]) {
if (temp > arrLength - 1) {
set.add(arrLength - 1);
} else {
set.add(temp);
}
temp++;
}
return set;
}
}
}
原因分析:
当在查询比数组中最小的数值还要小的数值时,程序会进入到下面这一步:
left=0,right=0,k=1;
而根据mid的计算公式:int mid = left + fibArray[k - 1];
可得出此时mid=1;
此时左指针left和右指针right均为0,而我们计算得到的中间指针mid却为1,这是不合理的!
而这种不合理性正是由于斐波那锲数列的第一个数字为1所导致的:
从划分的角度来看:当k=1时,此时,fibArray[k]=1,这表示我们已经无法再去划分数组了,而此时fibArray[k-1]=1意味着我们还可以把长度为1的数组划分,这就造成了错误的出现!
解决方案:
方法1.在递归查询之前将斐波那契额数组的第1个元素位置置0:
fibArray[0] = 0;
方法2:int mid = left + fibArray[k - 1]
以上两种方法都是为了避免出现left=0,right=0,而mid=1的情况出现