斐波那契查找(黄金分割法查找)(仅使用加减实现的二分查找)

什么是斐波那契查找

斐波那契数列,又称黄金分割数列,指的是这样一个数列:1、1、2、3、5、8、13、21、····,在数学上,斐波那契被递归方法如下定义:F(1)=1,F(2)=1,F(n)=f(n-1)+F(n-2) (n>=2)。该数列越往后相邻的两个数的比值越趋向于黄金比例值(0.618)。

斐波那契查找就是在二分查找的基础上根据斐波那契数列进行分割的。在斐波那契数列找一个等于略大于查找表中元素个数的数F[n],将原查找表扩展为长度为F[n](如果要补充元素,则补充重复最后一个元素,直到满足F[n]个元素),完成后进行斐波那契分割,即F[n]个元素分割为前半部分F[n-1]个元素,后半部分F[n-2]个元素,找出要查找的元素在那一部分并递归,直到找到。
斐波那契查找的时间复杂度还是O(log 2 n ),但是 与折半查找相比,斐波那契查找的优点是它只涉及加法和减法运算,而不用除法,而除法比加减法要占用更多的时间,因此,斐波那契查找的运行时间理论上比折半查找小,但是还是得视具体情况而定。
对于斐波那契数列:1、1、2、3、5、8、13、21、34、55、89……(也可以从0开始),前后两个数字的比值随着数列的增加,越来越接近黄金比值0.618。比如这里的89,把它想象成整个有序表的元素个数,而89是由前面的两个斐波那契数34和55相加之后的和,也就是说把元素个数为89的有序表分成由前55个数据元素组成的前半段和由后34个数据元素组成的后半段,那么前半段元素个数和整个有序表长度的比值就接近黄金比值0.618,假如要查找的元素在前半段,那么继续按照斐波那契数列来看,55 = 34 + 21,所以继续把前半段分成前34个数据元素的前半段和后21个元素的后半段,继续查找,如此反复,直到查找成功或失败,这样就把斐波那契数列应用到查找算法中了。

从图中可以看出,当有序表的元素个数不是斐波那契数列中的某个数字时,需要把有序表的元素个数长度补齐,让它成为斐波那契数列中的一个数值,当然把原有序表截断肯定是不可能的,不然还怎么查找。然后图中标识每次取斐波那契数列中的某个值时(F[k]),都会进行-1操作,这是因为有序表数组位序从0开始的,纯粹是为了迎合位序从0开始。所以用迭代实现斐波那契查找算法如下:

public class FibonacciSearch {

	/**
	 * @param args
	 */
	public final static int MAXSIZE = 20;

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] f = fibonacci();
		for (int i : f) {
			System.out.print(i + " ");
		}
		System.out.println();

		int[] data = { 1, 5, 15, 22, 25, 31, 39, 42, 47, 49, 59, 68, 88 };

		int search = 39;
		int position = fibonacciSearch(data, search);
		System.out.println("值" + search + "的元素位置为:" + position);
	}

	/**
	 * 斐波那契数列
	 * 
	 * @return
	 */
	public static int[] fibonacci() {
		int[] f = new int[20];
		int i = 0;
		f[0] = 1;
		f[1] = 1;
		for (i = 2; i < MAXSIZE; i++) {
			f[i] = f[i - 1] + f[i - 2];
		}
		return f;
	}

	public static int fibonacciSearch(int[] data, int key) {
		int low = 0;
		int high = data.length - 1;
		int mid = 0;

		// 斐波那契分割数值下标
		int k = 0;

		// 序列元素个数
		int i = 0;

		// 获取斐波那契数列
		int[] f = fibonacci();

		// 获取斐波那契分割数值下标
		while (data.length > f[k] - 1) {
			k++;
		}

		// 创建临时数组
		int[] temp = new int[f[k] - 1];
		for (int j = 0; j < data.lengt敏感词emp[j] = data[j];
		}

		// 序列补充至f[k]个元素
		// 补充的元素值为最后一个元素的值
		for (i = data.length; i < f[k] - 1; i++) {
			temp[i] = temp[high];
		}

		for (int j : temp) {
			System.out.print(j + " ");
		}
		System.out.println();

		while (low <= high) {
			// low:起始位置
			// 前半部分有f[k-1]个元素,由于下标从0开始
			// 则-1 获取 黄金分割位置元素的下标
			mid = low + f[k - 1] - 1;

			if (temp[mid] > key) {
				// 查找前半部分,高位指针移动
				high = mid - 1;
				// (全部元素) = (前半部分)+(后半部分)
				// f[k] = f[k-1] + f[k-1]
				// 因为前半部分有f[k-1]个元素,所以 k = k-1
				k = k - 1;
			} else if (temp[mid] < key) {
				// 查找后半部分,高位指针移动
				low = mid + 1;
				// (全部元素) = (前半部分)+(后半部分)
				// f[k] = f[k-1] + f[k-1]
				// 因为后半部分有f[k-1]个元素,所以 k = k-2
				k = k - 2;
			} else {
				// 如果为真则找到相应的位置
				if (mid <= high) {
					return mid;
				} else {
					// 出现这种情况是查找到补充的元素
					// 而补充的元素与high位置的元素一样
					return high;
				}
			}
		}
		return -1;
	}
}


全部评论
娃哥不错啊 学习了
点赞 回复
分享
发布于 2015-11-12 17:14
很好啊!不过有点不太懂,回去好好的看看
点赞 回复
分享
发布于 2016-09-14 19:24
联易融
校招火热招聘中
官网直投
人家注释说明都写错了,你还一股脑抄
点赞 回复
分享
发布于 2019-06-11 11:41

相关推荐

17 12 评论
分享
牛客网
牛客企业服务