Fibonacci查找

简介

  • Fibonacci查找是二分查找算法一种变形, 二分查找的中值为m
m = (start + end) / 2 =  start +  (end-start) / 2
  • 二分查找每次都是折半查找, 即系数为1/2
  • Fibonacci的取值是根据数组的黄金分割比进行分割

原理

  • 假设我们有数组arr, 数组长度刚好为 F(n), 根据
F(n) = F(n-1) + F(n-2)
  • 知道可以将数组分为长度为F(n-1), 和F(n-2)的两个子数组
  • 但是这样还不够,还缺少了中值m, 所以切分成两个是不够的, 还需要切分多一个长度为1的数组,包含中值m
{F(n)} = {F(?)} + [1] + {F(?)}
  • F(n)公式,两边减一
F(n)  - 1
= F(n-1) + F(n-2) - 1
= (F(n-1) -1) +  (1) + (F(n-2) - 1)
  • 令 A(n) = F(n) -1, 可以得到
A(n) = A(n-1) + 1 + A(n-2)
  • 所以最终是要构造长度为 A(n) = F(n) -1的数组
  • 然后不断的切分为长度分别为 A(n-1)= F(n-1) -1, 1 , A(n-2)= F(n-2) -1的子数组,去1对应的中值进行比较
  • 于是得到中值公式
m = start + A(n-1)
  • 然后进行比较,查找key对应的位置
    • key > arr[m]
      • 此时key位于 A(n-2)的区间
      • 所以 n = n-2, start = m + 1;
      • 继续查找
    • key < arr[m]
      • 此时key位于 A(n-1)的区间
      • 所以 n = n-1, end = m – 1;
      • 继续查找
    • key = arr[m], 那么m 就是要查找的索引,结束

实现

  • 首先给定的arr长度不一定是 A(n)=F(n) -1, 所以我们需要构造一个 长度为A(n) 的新数组,长度不够的补最大值
  • 最后查找的时候,如果超过原有数组长度,那么直接取原数组最大的索引就行了
    1. 准备Fibonacci函数
    1. 填充原有数组
    1. 实现二分查找
    1. 改造

实现Fibonacci

  • F的实现
function  Fibonacci(n) {
	var temp = [0, 1];
	var i = 2;
	while (i <= n) {
		temp[i] = temp[i-1] + temp[i-2];
		i++;
	}
	return temp[n];
}
  • 由于我们要经常用这个函数,所以应该直接返回 temp数组,否则会影响效率,改造一下,根据数组长度返回Fibonacci的结果temp
// 根据 F(n-1) -1 < arrLength <= F(n) -1
function Fibonacci (len) {
    var temp = [0, 1];
    var i = 1; 
    while (len > (temp[i] -1 )) {
        i++;
        temp[i] = temp[i-1] + temp[i-2]
    }
    return temp
}

拓展数组

function expand(arr, len) {
   var max = arr[arr.length -1];
    var i = arr.length;
    arr = arr.slice();
   while (i < len) {
        arr[i] = max;
        i++;
   }
   return arr;
}

实现个二分查找

function Search(arr, key) {
	var start = 0;
	var end = arr.length - 1;
	var m = 0;
	while (start <= end) {
			m = parseInt((start + end) / 2);
			 if (key > arr[m]) {
			 			start = m + 1;
			 			continue;
			 }
			 if (key < arr[m]) {
			 			end = m - 1;
			 			continue;
			 }
			 return m;
	}
	return -1;
}

改造

function FibonacciSearch(arr, key) {
	var max = arr.length -1;
	var F = Fibonacci (arr.length);
	var n = F.length -1; 
	arr = expand(arr, F[n] -1); // A[n] = F[n] - 1;
	
	var start = 0;
	var end = max; 
	var m = 0;
	while (start <= end) {
			m = start + F[n-1] -1; // 1 A[n-1] = F[n-1] - 1;
			 if (key > arr[m]) {
			 			start = m + 1;
			 			n = n -2; //2 
			 			continue;
			 }
			 if (key < arr[m]) {
			 			end = m - 1;
			 			n  = n -1;//3
			 			continue;
			 }
			 return Math.min(m, max); // 最后的结果不能超过max
	}
	return -1;
}

总结

  • 关于时间复杂度是O(logn), 这个我就不测试了。
  • 不过平均性能来说比二分查找要好
  • 如果key位于两端,就会比二分查找慢。
  • ps: 本人也是刚好复习算法,写出来加深印象。
点赞