斐波那契查找
黄金分割点:是指把一条线段分割为两部分,使其中一部分与全长之比等于另一部分与这部分之比,其比值是一个无理数,用分数表示为(√5-1)/2,取其前三位数字的近似值是0.618
斐波那契数列:F(0)=1,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*),随着数列项数的增加,前一项与后一项之比越来越逼近黄金分割的数值
原理
1.思路类似二分查找,前提:待查数列要求有序
2.仅仅改变了midIndex的位置,midIndex位于黄金分割点附近,即midIndex=left+F(k-1)-1
二分查找的midIndex = left + 1 / 2 * (right - left),斐波那契查找相当于把1 / 2替换为黄金分割数
对F(k-1)-1的理解:
1.通过斐波那契 数列F[k]=F[k-1]+F[k-2] 的性质,可以得到(F[k]-1)=(F[k-1]-1)+(F[k-2]-1)+1
该式说明:只要顺序表的长度为F[k]-1,则可以将表分为长度为F[k-1]-1和F[k-2]-1的两端。从而中间位置为 midIndex=left+F(k-1)-1
2.类似的每个子段也可以使用相同的方式分割
思路
1.把一个斐波那契数列映射到原需要查询的数列,原数列进行划分的规则就是借助于一个斐波那契数列
2.通过将斐波那契数列中的每一项的值看成是一个数列中元素的个数,其中,斐波那契数列中的每一项减去1就代表原数列中的下标,因为原数列起始下标是从0开始的。
3.由于斐波那契数列中的每一项我们看成是一个数列的元素个数,而斐波那契数列中的每一项的值是固定了的,但它却不包含所有的值。所以我们需要将原需要查询的数列扩容到大小离斐波那契数列中最接近的那一个数。
4.原数组长度是不能改变的,我们只能创建一个新的数组来复制arr数组的值并复制最后一个元素添加到末尾
代码实现
import java.util.ArrayList;
import java.util.Arrays;
public class FibonacciSearch {
public static int[] fibonacciSequence(int maxSize) {//获取斐波那契数列
int[] f = new int[maxSize];
f[0] = 1;
f[1] = 1;
for (int i = 2; i < maxSize; i++) {
f[i] = f[i - 1] + f[i - 2];
}
return f;
}
public static ArrayList<Integer> circleFibonacciSearch(int[] array, int find) {
int left = 0;
int right = array.length - 1;
int k = 0;//斐波那契数列中分割值的下标
int f[] = fibonacciSequence(20);
ArrayList<Integer> indexList = new ArrayList<>();//存放符合find的元素的索引
if (array == null || array.length == 0) {
return indexList;//数组为空
} else if (array.length == 1) {
if (find == array[0]) {//
indexList.add(0);
}
return indexList;
}
while (array.length > f[k]) {//获取斐波那契数列中分割值的下标,分割值即temp数组的大小
k++;
}
//创建大小为f[k]的temp数组
int[] temp = Arrays.copyOf(array, f[k]);
//f[k]若大于array.length,temp多余的部分用array[right]填充
for (int i = array.length; i <= temp.length - 1; i++) {
temp[i] = array[right];
}
while (find >= array[left] && find <= array[right]) {
int midIndex = left + f[k - 1] - 1;
int mid = temp[midIndex];
//每次向左边查找就是将f[k-1]中的k换为k-1,每向右边查找一次就是将f[k-1]中的k换为k-2
if (find < mid) {//向左查找
right = midIndex - 1;
//左方有f[k-1]个元素,可继续拆分f[k-1]=f[k-2]+f[k-3],即下次循环midIndex=left+f[(k-1)-1]-1
k -= 1;
} else if (find > mid) {//向右查找
left = midIndex + 1;
//右方有f[k-2]个元素,可继续拆分f[k-2]=f[k-3]+f[k-4],即下次循环midIndex=left+f[(k-2)-1]-1
k -= 2;
} else {//find == mid
if (midIndex > right) {//mid取到了填充数组末尾的重复元素了
addIndex(indexList, right, array, find);
return indexList;
} else {//midIndex <=right,mid没有取到填充数组末尾的重复元素
addIndex(indexList, midIndex, array, find);
return indexList;
}
}
}
return indexList;//find不存在
}
//封装方法,把等于find的元素的索引加入indexList
public static void addIndex(ArrayList<Integer> indexList, int midIndex, int[] array, int find) {
indexList.add(midIndex);//中间值本身的索引放入indexList中
int temp = midIndex - 1;
while (true) {//中间值向左寻找相同值的索引
if (temp < 0 || array[temp] != find) {
break;
}
indexList.add(temp--);
}
temp = midIndex + 1;
while (true) {//中间值向右寻找相同值的索引
if (temp > array.length - 1 || array[temp] != find) {
break;
}
indexList.add(temp++);
}
}
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战