斐波那契查找是一种基于斐波那契数列的查找算法,用于在有序数组中查找目标元素的位置。与二分查找类似,斐波那契查找也是一种分治算法,它通过比较目标值与数组的中间元素来确定下一步的查找范围。
在介绍斐波那契查找算法之前,我们先介绍一下很它紧密相连并且大家都熟知的一个概念——黄金分割。
黄金比例又称黄金分割,是指事物各部分间一定的数学比例关系,即将整体一分为二,较大部分与较小部分之比等于整体与较大部分之比,其比值约为1:0.618或1.618:1。
0.618被公认为最具有审美意义的比例数字,这个数值的作用不仅仅体现在诸如绘画、雕塑、音乐、建筑等艺术领域,而且在管理、工程设计等方面也有着不可忽视的作用。因此被称为黄金分割。
在数学中有一个非常有名的数学规律:斐波那契数列:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89…….
(从第三个数开始,后边每一个数都是前两个数的和)。
然后我们会发现,随着斐波那契数列的递增,前后两个数的比值会越来越接近0.618,利用这个特性,我们就可以将黄金比例运用到查找技术中。
基本思想:也是二分查找的一种提升算法,通过运用黄金比例的概念在数列中选择查找点进行查找,提高查找效率。同样地,斐波那契查找也属于一种有序查找算法。
斐波那契查找也是在二分查找的基础上进行了优化,优化中间点mid的计算方式即可
算法步骤如下:
首先,确定一个斐波那契数列。斐波那契数列的前两个数是1和1,后续的数是前两个数之和(1, 1, 2, 3, 5, 8, 13, 21, …)。
找到一个等于或大于数组长度的斐波那契数F[k],其中k是一个非负整数。可以通过递推或迭代计算斐波那契数列,直到找到满足条件的F[k]。
将数组扩展为长度为F[k],并将多出的位置值设为数组最后一个元素的值(即将数组填充到斐波那契数列的长度)。
设置两个指针:低指针low和高指针high,初始时分别指向数组的首位和末位。
比较目标值与数组的中间元素arr[mid],其中mid = low + F[k-1] - 1。
如果目标值等于arr[mid],则找到了目标元素,返回mid。
如果目标值小于arr[mid],则目标元素在低指针low和mid之间,将高指针high更新为mid-1,并将k减1。
如果目标值大于arr[mid],则目标元素在mid和高指针high之间,将低指针low更新为mid+1,并将k减2。
重复步骤5,直到找到目标元素或低指针low大于高指针high为止。
如果找到目标元素,返回其位置;否则,返回-1,表示目标元素不存在于数组中。
斐波那契查找的时间复杂度为O(log n),与二分查找相当,但在某些情况下,斐波那契查找具有更好的性能表现。它适用于较大的有序数组,并且对于元素分布不均匀的情况下,查找性能更稳定。
需求:定义一个方法利用斐波那契查找,数据如下:{2, 5, 8, 12, 16, 23, 38, 56, 72, 91};
要求:查询某个元素是否存在
代码如下:
package text.text02;
/*
斐波那契查找:
也是二分查找的一种提升算法,通过运用黄金比例的概念在数列中选择查找点进行查找,提高查找效率。同样地,斐波那契查找也属于一种有序查找算法。
斐波那契查找也是在二分查找的基础上进行了优化,优化中间点mid的计算方式即可
*/
public class text09A {
public static void main(String[] args) {
int[] arr = {2, 5, 8, 12, 16, 23, 38, 56, 72, 91};
//定义两个要查询的数(一个能查到,一个查不到)
int target1 = 23;
int target2 = 48;
//调用fibonacciSearch方法
int result1 = fibonacciSearch(arr, target1);
int result2 = fibonacciSearch(arr, target2);
//调用judge方法,并将fibonacciSearch方法的返回值和要查找的数作为参数
judge(result1, target1); //23存在数组中,其索引为:5
judge(result2, target2); //48不存在于数组中
}
public static int fibonacciSearch(int[] arr, int target) {
//定义一个变量记录数组的总长度
int n = arr.length;
//初始化斐波那契数列
int fib2 = 0; // 第二个斐波那契数初始化为0
int fib1 = 1; // 第一个斐波那契数初始化为1
int fib = fib1 + fib2; // 当前斐波那契数
//构建斐波那契数列,使得最大斐波那契数不超过数组长度
//循环直到找到最大的斐波那契数
while (fib < n) {
fib2 = fib1; //将第二个斐波那契数更新为前一个斐波那契数
fib1 = fib; //将第一个斐波那契数更新为当前斐波那契数
fib = fib1 + fib2; // 计算新的当前斐波那契数
}
int offset = -1; // 保存数组的偏移量
//斐波那契数列中进行迭代搜索
while (fib > 1) {
int i = Math.min(offset + fib2, n - 1); //计算当前位置
//根据当前位置与目标值的关系进行调整。
//如果找到目标值,返回索引位置
if (arr[i] == target) {
return i;
}
//如果当前值小于目标值,调整斐波那契数列以向后搜索
else if (arr[i] < target) {
fib = fib1;
fib1 = fib2;
fib2 = fib - fib1;
offset = i;
}
//如果当前值大于目标值,调整斐波那契数列以向前搜索
else {
fib = fib2;
fib1 = fib1 - fib2;
fib2 = fib - fib1;
}
}
//检查当前斐波那契数是否为1,并且下一个元素是否为目标值。
//如果是,返回偏移量加1的索引位置。
if (fib1 == 1 && arr[offset + 1] == target) {
return offset + 1;
}
//如果未找到目标值,返回-1表示未找到。
return -1;
}
//定义一个方法根据interpolationSearch方法的返回值判断
public static void judge(int index, int number) {
if (index == -1) {
System.out.println(number + "不存在于数组中");
} else {
System.out.println(number + "存在数组中,其索引为:" + index);
}
}
}
在这个示例中,我们定义了一个名为
fibonacciSearch
的静态方法,接受一个有序整数数组arr
和要查找的目标值target
作为参数。
在方法内部,我们首先确定一个不小于数组长度的斐波那契数
fib
,并且获取斐波那契数列中的第二个数fib2
和第一个数fib1
。
接下来,我们将当前的斐波那契数
fib
作为查找区间的大小。通过迭代,我们不断更新fib1
、fib2
和fib
,直到fib
不小于数组长度。此时,fib2
表示最后一个小于或等于数组长度的斐波那契数。
然后,我们使用偏移量
offset
来跟踪查找区间的起始位置,在每一次迭代中,我们根据目标值与数组中间元素的比较结果,将查找区间调整到适当的位置。
如果找到目标值,我们返回其位置;如果斐波那契数
fib1
为1并且偏移位置的下一位等于目标值,我们也返回该位置;否则,返回-1,表示目标值不存在于数组中。
在
main
方法中,我们定义了一个示例数组arr
和要查找的目标值target
,然后调用fibonacciSearch
方法来执行查找操作。如果找到目标值,则打印其索引;否则,打印目标值不存在于数组中的消息。