算法:有序表查找--斐波那契查找

相比于折半查找,插值查找,斐波那契查找的实现相对复杂,下面详细介绍一下该算法:

既然叫斐波那契查找,首先得弄明白什么是斐波那契数列。斐波那契数列有一个重要的性质:

前一个数除以相邻的后一个数,比值无限接近黄金分割

原理详解

斐波那契查找与折半查找很相似,他是根据斐波那契序列的特点对有序表进行分割的。他要求开始表中记录的个数为某个斐波那契数减1,即n=F[k]-1;

算法核心:

精髓采用最接近查找长度的斐波那契数值来确定拆分点。

举个例子来讲,现有长度为9的数组,要对它进行拆分,对应的斐波那契数列(长度先随便取,只要最大数大于9即可){1,1,2,3,5,8,13,21,34},不难发现,大于9且最接近9的斐波那契数值是f[7]=13,为了满足所谓的黄金分割,所以它的第一个拆分点应该就是f[7]的前一个值f[6]=8,即待查找数组array的第8个数,对应到下标就是array[8],依次类推。
 

推演到一般情况,假设有待查找数组array[n]和斐波那契数组F[k],并且n满足n>=F[k]-1&&n < F[k+1]-1,则它的第一个拆分点middle=F[k-1],即mid = low+F[k-1]-1。所以我们可以得到:当n=F[k]-1时,mid = low+F[k-1]-1。当n=F[k-2]-1时,mid = low+F[k-3]-1,令k=k-2,则mid = low+F[k-1]-1。

注意:如果n刚好等于F[k]-1,即待查找数组刚好拆成F[k-1]和F[k-2]两部分,此时正好拆分成两半;然而大多数情况不尽人意,n会小于F[k]-1,这时候要拆成完整F[k-1]和残疾的F[k-2]两部分,需要将不满的数值补齐。

for (i = n; i

比较结果也分为三种:

1)相等,mid位置的元素即为所求

 2)>  ,low=mid+1,k=k-2;说明:low=mid+1说明待查找的元素在[mid+1,hign]范围内,k=k-2 说明范围[mid+1,high]内的元素个数为n-F[k-1]= F[k]-1-F[k-1]=F[k-1]+F[k-2]-F(k-1)-1=F[k-2]-1个。通过k=k-2得到剩下的元素个数为F[k-2]-1个,因此正好mid = low+F[k-1]-1。

 3)<   ,high=mid-1,k=k-1;说明:low=mid+1说明待查找的元素在[low,mid-1]范围内,k=k-1 说明范围[low,mid-1]内的元素个数为F[k-1]-1个。

 

大部分说明都忽略了一个条件的说明:n=F(k)-1, 表中记录的个数为某个斐波那契数减1。这是为什么呢?

我想了很久,终于发现,原因其实很简单:

是为了格式上的统一,以方便递归或者循环程序的编写。表中的数据是F(k)-1个,使用mid值进行分割又用掉一个,那么剩下F(k)-2个。正好分给两个子序列,每个子序列的个数分别是F(k-1)-1与F(k-2)-1个,格式上与之前是统一的。

条件:

1)数据必须采用顺序存储结构;

2)数据必须有序

原理:

1)最接近查找长度的斐波那契值来确定拆分点

2)黄金分割

时间复杂度:

与拆半查找一样,也是logn,注意:斐波那契查找是纯加减运算实现的二分查找。

实现:来自《大话数据结构》

/* 斐波那契查找 */
int Fibonacci_Search(int *a, int n, int key)
{
	int low, high, mid, i, k = 0;
	low = 1;	/* 定义最低下标为记录首位 */
	high = n;	/* 定义最高下标为记录末位 */
	while (n>F[k] - 1)
		k++;
	for (i = n; ia[mid])
		{
			low = mid + 1;
			k = k - 2; /*下标减2*/
		}
		else
		{
			if (mid <= n)
				return mid;		/* 若相等则说明mid即为查找到的位置 */
			else
				return n;      /*若mid>n说明是补全数值,返回n*/
		}

	}
	return 0;
}

 

你可能感兴趣的:(c/c++,数据结构与算法)