【转】七大查找算法总结

参考博客


1.顺序查找

思路:从数据结构线形表的一端开始,顺序扫描,依次将扫描到的结点关键字与给定值k相比较,若相等则表示查找成功;若扫描结束仍没有找到关键字等于k的结点,表示查找失败。


2.二分查找

思路:也称为折半查找,属于有序查找算法。用给定值k先与中间结点的关键字比较,中间结点把线形表分成两个子表,若相等则查找成功;若不相等,再根据k与该中间结点关键字的比较结果确定下一步查找哪个子表,这样递归进行,直到查找到或查找结束发现表中没有这样的结点。

版本1.普通while判断版本
int BinarySearch1(int a[], int value, int n)
{
	if(n<=0)
		return -1;
	int begin=0;
	int end=n-1;
	while(begin<=end)
	{
		int mid=(begin+end)/2;
		if(a[mid]==value)
			return mid;
		else if(a[mid]<value)
			begin=mid+1;
		else
			end=mid-1;	
	}
	return -1;    //这里要注意!!
}
版本2.递归版本
int BinarySearch2(int a[], int value, int begin, int end)
{
	int mid=begin+(end-begin)/2;      //提倡这种写法!
	if(a[mid]==value)
		return mid;
	if(a[mid]<value)
		return BinarySearch2(a, value, mid+1,end);
	if(a[mid]>value)
		return BinarySearch2(a, value, begin, mid-1);
}

3.插值查找

mid=low+(key-a[low])/(a[high]-a[low])*(high-low)
思路:基于二分查找算法,将查找点的选择改进为自适应选择,可以提高查找效率。(说白了就是二分查找中mid取中点的方法太没有针对性,这里基于类似于百分比的方法,优化mid的取值,目的是提高效率)

int InsertSearch(int a[], int value, int begin, int end)
{
	int mid = begin+(value-a[begin])/(a[end]-a[begin])*(end-begin);      //核心改进之处!
	if(a[mid]==value)
		return mid;
	if(a[mid]<value)
		return BinarySearch2(a, value, mid+1,end);
	if(a[mid]>value)
		return BinarySearch2(a, value, begin, mid-1);
}

4.斐波那契查找(这个了解即可,不是很常见)

思路:也是二分查找的一种提升算法,通过运用黄金比例的概念在数列中选择查找点进行查找,提高查找效率。
具体来说,他是根据斐波那契序列的特点对有序表进行分割的。要求开始表中记录的个数为某个斐波那契数小1,也就是n=F(k)-1;


5.树表查找

5.1 最简单的树表查找算法——二叉树查找算法

二叉查找树,又称二叉搜索树,特点是:树的左分支的值小于右分支的值,通过中序遍历可得有序的数列。
思路:很直观,就是从根节点开始,进行值比对,而后根据大小情况向下走,直到找到目标值。
问题:插入和查找的时间复杂度均为O(logn),但是在最坏的情况下仍然会有O(n)的时间复杂度,原因在于插入和删除元素的时候,树没有保持平衡。

5.2 平衡查找树之2-3查找树

略(参见单独博客)

5.3 平衡查找树之红黑树

略(参见单独博客)


6.分块查找

思路:它是顺序查找的一种改进方法。
(具体来说是二分查找和顺序查找的结合产物,即首先在构建的索引表上面进行二分查找,而后在锁定的区间内进行顺序查找)
将n个数据元素"按块有序"划分为m块(m ≤ n)。每一块中的结点不必有序,但块与块之间必须"按块有序";即第1块中任一元素的关键字都必须小于第2块中任一元素的关键字;

step1.先选取各块中的最大关键字构成一个索引表;
step2 查找分两个部分:先对索引表进行二分查找或顺序查找,以确定待查记录在哪一块中;然后,在已确定的块中用顺序法进行查找。


7.哈希查找

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


附:哈希表的内容

1.散列表的构造方法

【1】直接定址法:H(k)=a*k+b
【2】数字选择法:如果关键字为数字且已知散列表中可能出现的关键字集合,则可选取其中数字分布比较均匀的若干位作为散列地址;
【3】平方取中法;
【4】折叠法(又细分为移位叠加和边界叠加);
【5】除留余数法:假设散列表长为m,选取适当的正整数p去除关键字,将所得余数作为散列地址:
H(key)=key%p ,(p<=m);
一般来说,p应该选为小于或等于散列表长度m的某个最大素数。(这种限定应该是基于解决冲突的考量)
【6】基数转换法;
【7】随机数法:H(key)=random(key)%m;

2.处理冲突的方法

【1】开放地址法
思路:首先将表置空,并根据某种方法在散列表中定义一个探查序列,当发生冲突时,沿此序列逐个单元查找空单元,一旦找到,则将新记录放入。
设表长为m,关键字个数为n,探查序列设为di,i=1,2,…,m,则开放地址公式为:hi=(H(key)+di)%m
探查序列的形成方法:
【1.1】线性探查法
基本思想:将散列表看成一个环形表,若发生冲突的单元地址为d,则依次探查d+1,d+2,…m-1,0,1,…d-1,直到找到一个空单元为止。
(具体操作方面,我们一般设定装填因子α为[0.65,0.9],通过公式m=n/α,由于n是给定的数据的个数,这样就可以得到散列表的长度m,然后d的范围基于m就知道了)
线性探查法不可避免的会出现“堆积”现象,具体来说就是:散列地址不同的结点争夺同一个后继散列地址;
【1.2】二次探查法(其实就是左右交替的探查)
公式为:
h2i-1=(H(key)+i2)%m
h2i=(H(key)-i2)%m
结论:这种方法可以减少堆积现象,但是不容易探查到整个散列表空间。仅当表长m为4j+3(j为正整数)的素数时,才能探查到整个表空间;
【1.3】随机探查法
hi=(H(key)+Ri)%m
其中R1,R2…Rm-1是1,2,…,m-1的一个随机序列

【2】拉链法
思路:将所有关键字为同义词的结点链接到同一个单链表中。
优势:
01.相比较而言,虽然拉链法所用的空间比开放地址法多,但是不会产生堆积现象,其平均查找长度也较短。
02.对于用拉链法构造的散列表,简单地在链表上删除结点即可实现散列表的删除操作,而在开放地址法构造的散列表中,并不能简单地将被删结点的空间置空来删除结点,因为空地址单元都是查找失败的条件,这样做会截断在它之后填入散列表的同义词的查找路径,正确的做法是在被删除结点上做删除标记,而不能真正删除结点。
在这里插入图片描述
结论:散列表的平均查找长度是装填因子α的函数,与记录个数无关
这一点是很好理解的,因为前面有公式:m=n/α,这说明了m的长度是基于α得到的,因此在第一步基于散列函数做搜索时,都是O(1),但是在解决冲突时,搜索的长度就只和α有关了;(相当于余数的思想)

(因此通过选择合适的α可以将散列表的查找长度限定于某一个范围,显然α越小,产生冲突的机会越小,但是过小的α会造成较大的空间浪费)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值