24、查找算法-斐波那契查找

来源:https://www.bilibili.com/video/BV1B4411H76f?p=77

一、思路

斐波那契查找:也可以说是黄金分割法查找,斐波那契序列{1,1,2,3,5,8,13...},相邻两个数的比值无限接近于黄金分割值(0.618)。这里还是对中间下标mid进行修改。

斐波那契满足的性质是这样的: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,剩下那个1就是一个中间值。现在,长度划分好了。有两个问题:原始长度不满足F(k)-1怎么办?mid下标怎么计算?

原始长度可以通过扩充的形式来增长,增长的部分用原始数组的最后一个数填补;

mid=low+F(k-1)-1,即左边是较长的一部分,右边较短。

二、实现

 1 //斐波那契查找
 2 public class FibonacciSearch {
 3     //给定一个斐波那契数组的最大长度
 4     public static int MAXSIZE = 20;
 5 
 6     public static void main(String[] args) {
 7         int[] arr = {1,8,10,89,1000,1234};
 8         System.out.println(Arrays.toString(arr));
 9 
10         int a = fibSearch(arr,89);
11         System.out.println(a);
12     }
13 
14     //给定斐波那契的所有情况
15     public static int[] fib(){
16         int[] f = new int[MAXSIZE];
17         f[0] = 1;
18         f[1] = 1;
19         for (int i = 2; i < MAXSIZE; i++) {
20             f[i] = f[i-1]+f[i-2];
21         }
22         return f;
23     }
24 
25     public static int fibSearch(int[] arr,int finalVal){
26         int left = 0;
27         int right = arr.length - 1;
28         //长度补齐
29         int[] fib = fib();
30         int k = 0;//找长度
31         while(right > fib[k] - 1){
32             k++;
33         }
34         int[] temp = Arrays.copyOf(arr,fib[k]);//补齐
35         for (int i = right + 1; i < temp.length; i++) {
36             temp[i] = arr[right];
37         }
38 
39         int mid = 0;
40         while (left <= right){
41             mid = left + fib[k-1] - 1;
42             if(finalVal < temp[mid]){
43                 //向左找
44                 right = mid - 1;
45                 //按照mid的计算方式,左边是较长的序列,其长度对应的是k-1这个下标的前一个k-1-1
46                 k -= 1;
47             }else if (finalVal > temp[mid]){
48                 //向右找
49                 left = mid + 1;
50                 //按照mid的计算方式,右边是较短的序列,其长度对应的是k-1这个下标的前两个k-1-2
51                 k -= 2;
52             }else {
53                 //因为之前扩容过,得看找到的这个mid在不在扩容的范围里面(返回的是下标,在扩容的里面就是数组最后一个)
54                 if(mid <= right){
55                     //不属于扩容的范围
56                     return mid;
57                 }else {
58                     return right;
59                 }
60             }
61         }
62         return -1;
63     }
64 }

这里用递归好像反而不好整了,需要单独将补齐的工作分开,输入进去的应该是补齐后的数组。输出的时候也要单独判断在不在扩充区。

猜你喜欢

转载自www.cnblogs.com/zhao-xin/p/13168448.html