数据结构实验——实现插值和斐波那契查找

实现插值和斐波那契查找 实验

1.1 实验内容

所谓查找(Search)又称检索,就是在一个数据元素集合中寻找满足某种条件的数据元素。关于有序表的查找,有折半查找、插值查找、斐波那契查找等,它们的原理和实现方法各有不同,对不同数据的处理也各有优劣。

查找在计算机数据处理中是经常使用的操作。查找算法的效率高低直接关系到应用系统的性能。本次实验是在折半查找的代码基础上,实现插值查找和斐波那契查找,并比较不同的数据这三种方法的查找效率,得出初步结论。

1、插值查找

插值查找的算法思想同折半查找类似,区别在于求中间位置的公式,其求中间点的公式为:     

mid = low + (high - low) * (k - elem[low]) / (elem[high] - elem[low])

其中,k为给定值,elem[low]和elem[high]分别为查找区间中具有最小关键字和最大关键字的数据元素,它们分别在查找区间的两端。插值算法将折半查找的比例参数改进为自适应的根据关键字在整个有序表中所处的位置,让mid值的变化更靠近关键字key。

2、斐波那契查找

   斐波那契查找也是基于有字表的逐步缩小查找区间的查找方法。该方法的查找区间端点和中间点都与斐波那契数列有关。斐波那契数列的定义为:1,1,2,3,5,8,13,…。即价1)=1,f(2)=1,fi)=fi-l)+f(i-2)(当i>2时)。

斐波那契查找的理念:让mid保持在数组的黄金分割点处,mid前面长度为F[K-1]-1,后面长度为F[K-2]-1,数组总长度为F[K]-1,mid在黄金分割点。

1.2文件结构、开发环境等说明

开发环境版本

VisualStudio 2022

工程文件名

ex4_three_search.sln

头文件个数

4个

源程序文件个数

1个

文件名

文件类型

功能简介

备注

binsearch.h

头文件

折半查找算法实现

FibonacciSearch.h

头文件

斐波那契查找算法实现

InsertSearch.h

头文件

插值查找算法实现

Assistance.h

头文件

辅助软件包

test.cpp

源文件

测试文件

1.3 实现技术

1、插值查找算法

elem[]数组中存储了有序表中的n个数据,n个数据元素按其关键字从小到大排列。并在开始时,设查找区间的下限low-0,上限high=n-1。如果low>high,则表示查找失败,返回-1:否则进入while循环,进行查找。先求出查找区间分割处的数据元素下标mid = low + (high - low) * (k - elem[low]) / (elem[high] - elem[low]) ,用该数据元素的关键字elem[mid]与给定值key进行比较,比较的结果经过多个if-else判断语句:

①若elem[mid]==key,则查找成功,报告成功信息并返回其下标mid

②若elem[mid]<key,则说明如果数据表中存在要找的数据元素,该数据元素一定在mid的右侧,可把查找区间缩小到数据表的后半部分(low=mid+1),再继续进行插值查找。

③若elem[mid]>key,则说明如果数据表中存在要找的数据元素,该数据元素一定在mid的左侧。可把查找区间缩小到数据表的前半部分(high=mid-1)再继续进行查找。

template<class ElemType>
int InsertSearch(ElemType elem[], int n, ElemType key, int &count3)
// 操作结果: 迭代算法,在有序表中查找其关键字的值等于key的元素,如查找成功,则返回此元素的序号,否则返回-1
{
	int low = 0, high = n - 1;int mid;
	while (low <= high)
	{
		count3++;
		if (elem[high] - elem[low] != 0)
			mid = low + (high - low) * (key - elem[low]) / (elem[high] - elem[low]);
		else
			return -1;
		if (key == elem[mid])
		{
			count3++;
			return mid;
		}
		else if (key <= elem[mid])
		{
			high = mid - 1;
			count3++;
		}
		else
		{
			low = mid + 1;
			count3++;
		}
	}
	return -1;
}

2斐波那契算法

elem[]数组中存储了有序表中的n个数据,n个数据元素按其关键字从小到大排列。并在开始时,设查找区间的下限low-0,上限high=n-1。利用for循环生成斐波那契数列,并存储在fib[]数组中。while循环找到有序表元素个数在斐波那契数列中最接近的最大数列值,且如果n不满足斐波那契数列中某个元素的值,则用elem[high]补全有序表,后面的算法思想与折半查找类似:

1)如果查找区间长度小于(low>high),则表示查找失败,返回-1:否则继续以下步骤。

2)根据斐波那契数列求出查找区间中某位置的数据元素下标mid=f(k-1)-1

3)区间中间位置的数据元素的关键字elem[mid]与给定值key进行比较,比较的结果有以下三种可能。

①若elem[mid]==key,则查找成功,报告成功信息并返回其下标mid

②若elem[mid]<key,则说明如果数据表中存在要找的数据元素,该数据元素一定在mid的右侧,可把查找区间缩小到数据表的后半部分,得到的子表的长度正好为f(k-2)-1,再继续进行斐波那契查找。

③若elem[mid]>key,则说明如果数据表中存在要找的数据元素,该数据元素一定在mid的左侧。可把查找区间缩小到数据表的前半部分,得到的子表的长度正好为f(k-1)-1,再继续进行斐波那契查找。

//斐波那契查找
template<class ElemType>
int FibonacciSearch(ElemType elem[], int n, ElemType key, int &count2)
{
	int low = 0, high = n - 1;
	int mid, fib[MAXSIZE], k = 0;
	fib[0] = fib[1] = 1;
	for (int i = 2; i < MAXSIZE; i++)
		fib[i] = fib[i - 1] + fib[i - 2];
	//找到有序表元素个数在斐波那契数列中最接近的最大数列值
	while (high > fib[k] - 1)
		k++;
	//补齐有序表
	for (int i = n; i <= fib[k] - 1; i++)
		elem[i] = elem[high];
	while (low <= high)
	{
		count2++;
		mid = low + fib[k - 1] - 1;//根据斐波那契数列进行黄金分割
		if (key == elem[mid])
		{
			count2++;
			if (mid <= n - 1)
			{
				count2++;
				return mid;
			}
			else//说明得到的数据元素是补全值
			{
				return n - 1;
				count2++;
			}
		}
		if (elem[mid] > key)
		{
			count2++;
			high = mid - 1;
			k = k - 1;
		}
		if (elem[mid] < key)
		{
			count2++;
			low = mid + 1;
			k = k - 2;
		}
	}
	return -1;
}

1.4测试结果

1、有序表数据个数n较大,且分布比较均匀——插值查找

elem[10000] = {1,2,3,4,5,6,7,8,9,……,9993,9994,9995,9996,9997,9998,9999,10000}

key = 5

int elem[10000];
for (int i = 0; i < 10000; i++)
	elem[i] = i + 1;
int k = 5;

测试结果:

插值查找的比较次数相比于折半查找和斐波那契查找明显少了很多,说明在有序表的数据元素个数较大且分布较为均匀时用插值查找较为合适。

2、有序表数据个数n较大,且分布极端、不均匀——斐波那契查找/折半查找

elem[1500] = {1,2,3,4,5,6,7,8,9,……,2000,2001,2002,……,100496,199487,100498,100499};

key = 98943

for (int i = 0; i < 10; i++)
	elem[i] = i + 1;
for (int i = 10; i < 100; i++)
	elem[i] = i + 1990;
for (int i = 100; i < 1500; i++)
	elem[i] = i + 98999;
int k = 98943;

测试结果:

在1中数据较大且均匀的有序表用插值查找比较次数较少,但面对像2中的较为极端且不均匀的数据来说,插值算法的比较次数相比于另外两个查找方法就显得逊色很多。

可以看到在这种数据的情况下,折半查找和斐波那契查找的比较次数相差不大,与折半查找相比,斐波那契查找的优点是它只涉及加法和减法运算,而不用除法,而除法比加减法要占用更多的时间,因此,斐波那契查找的运行时间理论上比折半查找小。

若只考虑比较次数这一个因素,斐波那契查找和插值查找都较为合适,但要考虑时间因素,则更适合用斐波那契查找。

3、有序表数据个数n较小——折半查找

elem[] = { 1,2, 3 ,4,5,6,7,8,9,10,11,12,13,14,15, 23,34,39,43,46,48,50,56,60,67,68,71,75,83, 86,2000, 2003, 2013, 2040, 2043,2046,2049,2050, 2052,2054,2057,2059,2070 }

key = 23

int elem[] = { 1,2, 3 ,4,5,6,7,8,9,10,11,12,13,14,15,  23, 34, 39,43,46,48,50,56,60,67, 68, 71,75,83, 86,2000, 2003, 2013, 2040, 2043,2046,2049,2050, 2052,2054,2057,2059,2070 };

int k = 23;

测试结果:

 

当数据集较小时,用折半查找较为合适。

完整代码可看资源区

创作不易~麻烦点个赞~~谢谢大家~~~ 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值