算法-斐波那契查找

斐波那契查找是二分查找的一个变体,与插值查找一样,也是对二分查找中mid的重新定义与计算

首先我们来说说黄金分割点,简单的说就是:假如有一段长为l的绳子,在绳子上的某一点d切断该绳子,得到两根绳子,较长的那一根长度为为l1,较短的为l2,若l1/l = l2/l1 ≈ 0.618,那么我们称d为黄金分割点。

我们把绳子类比到数组,把点d类比到mid。也就是说,我们需要找出数组中的黄金分割点作为mid。

斐波那契数列刚刚好是一个非常适合作为寻找黄金分割点的工具!

斐波那契数列有两个特点

  • f(k) = f(k - 1) + f(k - 2) k > 2
  • f(k - 2) / f(k - 1) ≈ 0.618 k > 2

我们根据①可知:若所要查找的数组长度为f(k),则可以切分为:左半部分长度为f(k - 1),右半部分长度为f(k - 2)。根据②以及黄金分割点的定义可以知道如果按这样切分,那么这两部分的长度之比正是黄金分割点0.618!则此时mid可以定义为:mid = left + f(k - 1) - 1
数组中的黄金分割点

若数组本身的长度并没有任何大于2的常数,使得f(k)与之匹配呢?
那么我们需要找到一个k,使得 数组长度 <= f(k),对数组进行扩容,多出来的位置的值用原始数组最后一个元素的值填充。

算法代码


import java.util.Arrays;

public class FibonacciSearch {

	private static int[] myArr;//原始数组
	
	private static int[] extendArr; //扩容后的数组

	private static final int FIBONACCI_SEQUENCE_SIZE = 20;// 斐波那契数列长度

	private static int[] fibonacciSequence = new int[FIBONACCI_SEQUENCE_SIZE];// 斐波那契数列

	private int k = 2; // 原始数组扩容到fibonacciSequence[k],k >= 0

	static {
		initFibonacciSequence();
	}

	private FibonacciSearch() {
		extendArray();
	}

	public static FibonacciSearch instance(int[] arr) {
		myArr = arr;
		return new FibonacciSearch();
	}

	/**
	 * 
	 * @Title: initFibonacciSequence
	 * @Description: 初始化斐波那契数列
	 * @param:  参数说明
	 * @return: void 返回类型
	 */
	private static void initFibonacciSequence() {
		fibonacciSequence[0] = fibonacciSequence[1] = 1;
		for (int i = 2; i < FIBONACCI_SEQUENCE_SIZE; i++) {
			fibonacciSequence[i] = fibonacciSequence[i - 1] + fibonacciSequence[i - 2];
		}
	}

	/**
	 * 
	 * @Title: extendArray
	 * @Description: 对数组进行扩容
	 * @return: void
	 */
	private void extendArray() {
		int length = myArr.length;
		// 找到一个k,使得 数组长度 <= f(k),对数组进行扩容
		while (length > fibonacciSequence[k]) {
			k++;
		}

		extendArr = Arrays.copyOf(myArr, fibonacciSequence[k]);

		int extendArrLength = extendArr.length;
		// 多出来的位置的值用原始数组最后一个元素的值填充
		for (int j = length; j < extendArrLength; j++) {
			extendArr[j] = myArr[length - 1];
		}
	}

	/**
	 * 
	 * @Title: fibonacciSearch
	 * @Description: 利用斐波那契数列的递归二分查找
	 * @param: @param left 当前数组的第一个元素的下标
	 * @param: @param right 当前数组的最后一个元素的下标
	 * @param: @param value 要查找的值
	 * @param: @param k fibonacciSequence[k]是当前数组的长度,fibonacciSequence[k - 1]是左半部分长度,fibonacciSequence[k - 2]是右半部分长度
	 * @param: @return 参数说明
	 * @return: int 返回类型
	 * @throws
	 */
	private int fibonacciSearch(int left, int right, int value, int k) {

		// 如果当前数组不为空
		if (left <= right) {
			int mid = left + fibonacciSequence[k - 1] - 1;// 找到数组的中点下标mid
			// 如果所要查找的值比mid下标对应的值小,那么要向数组的左半部分进行查找
			if (value < extendArr[mid]) {
				right = mid - 1;
				k--;//见方法上对参数k的注释
				return fibonacciSearch(left, right, value, k);
			} else if (value > extendArr[mid]) {// 如果所要查找的值比mid下标对应的值大,那么要向数组的右半部分进行查找
				left = mid + 1;
				k-=2;//见方法上对参数k的注释
				return fibonacciSearch(left, right, value, k);
			} else {// 如果所要查找的值与mid下标对应的值相同
				if(mid <= myArr.length - 1)return mid;//如果mid没有落入到扩容的部分(扩容部分的元素值全都是原始数组的最后一个元素的值)
				return myArr.length - 1;//如果mid落入到扩容的部分(说明此时想查找的值是原始数组最后一个值),返回原始数组最后一个值的下标
			}
		}

		// 若所要查找的数组为空,则返回-1
		return -1;
	}
	
	public int fibonacciSearch(int value) {
		return fibonacciSearch(0, extendArr.length - 1, value, k);
	}

}

源码:github地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值