目录

斐波拉契查找

基本介绍

黄金分割点

黄金分割点是指把一条线段分割为两部分,使其中一部分与全长之比等于另一部分与这部分之比。取其前三位数字的近似值是0.618。由于按此比例设计的造型十分美丽,因此称为黄金分割,也称为中外比。这是一个神奇的数字,会带来意向不到的效果。

斐波拉契数列

斐波那契数列 {1, 1, 2, 3, 5, 8, 13, 21, 34, 55 } 发现斐波那契数列的两个相邻数的比例,无限接近黄金分割值0.618。

斐波那契查找和斐波那契数列有什么联系?

https://gitee.com/lienhui68/picStore/raw/master/null/20200625003430.png

斐波那契查找原理与二分查找和插值查找相似,仅仅改变了mid的位置,mid不再是中间或插值得到,而是位于黄金分割点附近,即mid=left+F(k-1)-1

为什么是F(k)-1

F(k)-1 = F(k-1)-1 + F(k-2)-1 + 1, mid占一个位置,刚好mid前面一个子序列占F(k-1)-1,后面占F(k-2)-1,格式统一,在循环程序里使用起来就很方便。

排序流程

  1. 构建一个合适的斐波拉契数组
  2. 构建一个临时数组,长度是F(k)-1且刚好等于或大于原始数组长度
  3. 拷贝原始数组到临时数组,临时数组多出来的位置用原始数组最后一位填充
  4. mid值使用low+F(k-1)-1
  5. 比较arr[mid]和key 5.1 key>arr[mid], left=mid+1,k-=2, 因为在新的left到right区间元素个数为F(k-2)-1,所以这里要k-=2; 5.2 key<arr[mid], right=mid-1,k–, 因为在left到新的right区间元素个数为F(k-1)-1,所以这里k–; 5.3 key==arr[mid], return

代码实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
    /**
     * 构建斐波拉契数组
     *
     * @param size
     * @return
     */
    private static int[] buildFibonacciArr(int size) {
        int[] f = new int[size];
        f[0] = 0;
        f[1] = 1;
        for (int i = 2; i < size; i++) {
            f[i] = f[i - 1] + f[i - 2];
        }
        return f;
    }

    /**
     * 斐波拉契查找
     *
     * @param arr
     * @param key
     * @return
     */
    public static int fibonacciSearch(int[] arr, int key) {
        int left = 0, right = arr.length - 1;
        // 1. 构建一个合适的斐波拉契数组
        int[] f = buildFibonacciArr(20);
        // 2. 构建临时数组
        // 2.1 确定临时数组长度也就是确定k值
        int k = 1; // 斐波拉契数组下标
        while (arr.length > f[k] - 1) {
            k++;
        }
        // 2.2 构建一个临时数组,长度是F(k)-1且刚好等于或大于原始数组长度,拷贝原始数组到临时数组
        int[] temp = Arrays.copyOf(arr, f[k] - 1);
        // 2.3 临时数组多出来的位置用原始数组最后一位填充
        for (int i = right + 1; i < temp.length; i++) {
            temp[i] = arr[right];
        }
        // 查找key所在位置
        while (left <= right) {
            int mid = left + f[k - 1] - 1;
            if (key > arr[mid]) {
                left = mid + 1;
                k -= 2; // 因为在新的left到right区间元素个数为F(k-2)-1,所以这里要k-=2;
            } else if (key < arr[mid]) {
                right = mid - 1;
                k--; // 因为在left到新的right区间元素个数为F(k-1)-1,所以这里k--;
            } else {
                if (mid < arr.length) {
                    return mid;
                } else {
                    return arr.length - 1;
                }
            }
        }
        return -1;
    }