数据结构(四)查找算法(c语言)

查找是数据处理经常进行的操作。这里我介绍常见的几种,主要有静态查找,动态查找,哈希查找。

静态查找有:二分查找,顺序查找,插值查找 ,斐波那契查找。

动态查找:主要针对二叉树。

哈希查找 :主要理解哈希查找的思想 。

第一种:二分查找 。

在一个查找区,确定中心位置后,用待查找的值与中心值比较,前者大,就把查找区锁定到后半段,反之,锁定在前半段。这样的过程一直进行,知道查找区边界结束 。

有序顺序表的二分查找程序如下 :

//二分查找
#include

int binsearch(int *ListSeq,int ListLength,int KeyData)
{
	int low = 0;
	int mid;
	int high = ListLength - 1;
	while(low <= high)
	{
		mid = (low + high)/2;
		if(KeyData < ListSeq[mid])
			high = mid -1;
		else if(KeyData > ListSeq[mid])
			low = mid + 1;
		else
			return mid;
	}
	return -1;
}
第二种:顺序查找

顺序查找:顾名思义就是,从第一个开始逐个把查找区的值与待查值比较,知道后边界结束。

程序如下:

//顺序查找
#include

int FindSeq(int *ListSeq,int ListLength,int KeyData)
{
	int i;
	for(i = 0;i < ListLength,i++)
	{
		if(ListSeq[i] == KeyData)
			return i;
	}
	return 0;
}

int main()
{
	int a[5] = {34,35,26,89,56};
	int Data = FindSeq(a,5,89);
	if(data != 0)
		printf("查找成功,是第%d个元素",Data);
	else
		printf("查找失败");
}
第三种:插值查找 

我们由二分查找知道,是折半查找,那么我们是不是可以折四分之一查找呢,或者更多,比如,你在英汉词典中查找“apply”,你一定是翻书的前面,而不是后面。如果查“zoo”,你会从书的最后面开始找,而不是,从中间去找 。

再例如,从数值范围为1~10000的100个元素从小到大均匀分布的数组中查找5,我们是不是只需要考虑从下标较小的开始查找,插值查找的中心思想。

程序如下:

//插值查找(二分查找加强版)


#include


int binsearch(int *ListSeq,int ListLength,int KeyData)
{
	int low = 0;
	int mid;
	int high = ListLength - 1;
	while(low <= high)
	{
		mid = low + (KeyData - ListSeq[low])/(ListSeq[high] - ListSeq[low])*(low + high);
		if(KeyData < ListSeq[mid])
			high = mid -1;
		else if(KeyData > ListSeq[mid])
			low = mid + 1;
		else
			return mid;
	}
	return -1;
}

第四种:斐波那契查找

斐波那契查找其实也是二分查找的一种提升算法,相对于二分查找,一般将待比较的key值与 第min = (low + high)/ 2 的位置比较,而,斐波那契查找比较结果分三种情况:

1,相等。

2,大于, low =  mid +1, k -= 2 ;

3,小于, high = high -1,k -= 1 ;

其实,斐波那契查找就是利用斐波那契数列的性质,斐波那契数列的前后两个数的比值越来越接近黄金比例0.618.所以一个长度只要可以被黄金分割,那么分割以后的片段继续黄金分割,循环。

程序如下:

//斐波那契查找
#include
#include

#define MAx 20

void Fibonacci(int *f)			//产生斐波那契数列
{
	int i;
	f[0] = 0;
	f[1] = 1;
	for(i = 2;i < MAX;++i)
	{
		f[i] = f[i-2] + f[i-1];
	}
}

int Fibonacci_search(int *a,int key;,int n)
{
	int i,j = 0;
	int low = 0;
	int mid;
	int high = n -1;
	int F[MAX];
	Fibonacci(F);
	while(n >F[j] -1)				//计算出n在斐波那契中的数列
		++j;
	for(i = n;i key)
		{
			high = mid -1;
			j = j - 1;
		}	
		else if(a[mid] 
第五种:希尔查找。

什么是哈希表(Hash)?

我们使用一个下标范围比较大的数组来存储元素。可以设计一个函数(哈希函数, 也叫做散列函数),使得每个元素的关键字都与一个函数值(即数组下标)相对应,于是用这个数组单元来存储这个元素;也可以简单的理解为,按照关键字为每一个元素"分类",然后将这个元素存储在相应"类"所对应的地方。但是,不能够保证每个元素的关键字与函数值是一一对应的,因此极有可能出现对于不同的元素,却计算出了相同的函数值,这样就产生了"冲突",换句话说,就是把不同的元素分在了相同的"类"之中。后面我们将看到一种解决"冲突"的简便做法。
总的来说,"直接定址"与"解决冲突"是哈希表的两大特点。

什么是哈希函数?
哈希函数的规则是:通过某种转换关系,使关键字适度的分散到指定大小的的顺序结构中,越分散,则以后查找的时间复杂度越小,空间复杂度越高。

算法思想:哈希的思路很简单,如果所有的键都是整数,那么就可以使用一个简单的无序数组来实现:将键作为索引,值即为其对应的值,这样就可以快速访问任意键的值。这是对于简单的键的情况,我们将其扩展到可以处理更加复杂的类型的键。

算法流程:
1)用给定的哈希函数构造哈希表;
2)根据选择的冲突处理方法解决地址冲突;
常见的解决冲突的方法:拉链法和线性探测法。
3)在哈希表的基础上执行哈希查找。

哈希表是一个在时间和空间上做出权衡的经典例子。如果没有内存限制,那么可以直接将键作为数组的索引。那么所有的查找时间复杂度为O(1);如果没有时间限制,那么我们可以使用无序数组并进行顺序查找,这样只需要很少的内存。哈希表使用了适度的时间和空间来在这两个极端之间找到了平衡。只需要调整哈希函数算法即可在时间和空间上做出取舍。

复杂度分析:
单纯论查找复杂度:对于无冲突的Hash表而言,查找复杂度为O(1)(注意,在查找之前我们需要构建相应的Hash表)。
使用Hash,我们付出了什么?
我们在实际编程中存储一个大规模的数据,最先想到的存储结构可能就是map,也就是我们常说的KV pair,经常使用Python的博友可能更有这种体会。使用map的好处就是,我们在后续处理数据处理时,可以根据数据的key快速的查找到对应的value值。map的本质就是Hash表,那我们在获取了超高查找效率的基础上,我们付出了什么?
Hash是一种典型以空间换时间的算法,比如原来一个长度为100的数组,对其查找,只需要遍历且匹配相应记录即可,从空间复杂度上来看,假如数组存储的是byte类型数据,那么该数组占用100byte空间。现在我们采用Hash算法,我们前面说的Hash必须有一个规则,约束键与存储位置的关系,那么就需要一个固定长度的hash表,此时,仍然是100byte的数组,假设我们需要的100byte用来记录键与位置的关系,那么总的空间为200byte,而且用于记录规则的表大小会根据规则,大小可能是不定的。


以上就是对查找算法的相关介绍,至于动态查找,这里不做详解。




你可能感兴趣的:(数据结构)