8.2基于线性表的查找法

8.2基于线性表的查找法

基于线性表的查找法具体可分为顺序查找法折半查找法以及分块查找法

一、顺序查找法

顺序查找法的特点是,用所给关键字与线性表中各元素的关键字逐个比较,直到成功或失败。存储结构通常为顺序结构,也可为链式结构。
顺序结构数据类型的定义:

#define LIST_SIZE 20
typedef struct {
	KeyType key;
	OtherType other_data;
}RecordType;
typedef struct {
	RecordType r[LIST_SIZE+1]; /* r[0]为工作单元 */
	int length;
}RecordList;

[算法思想]
在表的一端设置一个称为“监视哨”的附加单元,存放要查找元素的关键字。从表的另一端开始查找,如果在“监视哨”找到要查找元素的关键字,返回失败信息,否则返回相应下标。

[算法描述]
【算法 1 设置监视哨的顺序查找法】

int SeqSearch(RecordList l, KeyType k)
/*在顺序表 l 中顺序查找其关键字等于 k 的元素,若找到,则函数值为该元素在表中的
位置,否则为 0*/
{
	l.r[0].key=k; i=l.length;
	while (l.r[i].key!=k) i--;
	return(i);
}

其中 l.r[0] 为“监视哨”,可以起到防止越界的作用。

[算法分析]
用平均查找长度(ASL)分析顺序查找算法的性能。假设列表长度为 n,那么查找第 i个数据元素时需进行 n-i+1 次比较,即 Ci=n-i+1。又假设查找每个数据元素的概率相等,即Pi=1/n,则顺序查找算法的平均查找长度为:

在这里插入图片描述
为便于比较,下面给出不用“监视哨”的算法。

【算法 2 不设置监视哨的顺序查找法】

int SeqSearch(RecordList l, KeyType k)
/*不用“监视哨”法,在顺序表中查找关键字等于 k 的元素*/
{
	i=l.length;
	while (i>=1&&l.r[i].key!=k) i--;
	if (i>=1) 
		return(i)
	else 
		return (0);
}

算法 2 与算法 1 相比,循环控制条件中增加了 i>=1,用以判断查找过程是否越界。加上“监视哨”可省去这个条件,从而提高查找效率。

二、折半查找法

折半查找法又称为二分查找法,这种方法对待查找的列表有两个要求:

  • (1)必须采用顺序存储结构;
  • (2)必须按关键字大小有序排列。

[算法思想]:
首先,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。

重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。

下图给出了用折半查找法查找 12、50 的具体过程,其中 mid=(low+high)/2,当 high<low时,表示不存在这样的子表空间,查找失败。
在这里插入图片描述
在这里插入图片描述
[算法描述]:

int BinSrch (RecordList l, KeyType k)
/*在有序表 l 中折半查找其关键字等于 k 的元素,若找到,则函数值为该元素在表中的
位置*/
{
	low=1 ; high=l.length;/*置区间初值*/
	while ( low<=high)
	{
		mid=(low+high) / 2;
		if (k==l.r[mid]. key) 
			return(mid);/*找到待查元素*/
		else if (k<l.r[mid]. key) 
			high=mid-1;/*未找到,则继续在前半区间进行查找*/
		else 
				low=mid+1;/*继续在后半区间进行查找*/
	}
	return (0);
}

[算法分析]:
用平均查找长度(ASL)分析折半查找算法的性能。

折半查找过程可用二叉判定树的描述,判定树中每一结点对应表中一个记录,但结点值不是记录的关键字,而是记录在表中的位置序号。根结点对应当前区间的中间记录,左子树对应前一子表,右子树对应后一子表。

显然,找到有序表中任一记录的过程,对应判定树中从根结点到与该记录相应的结点的路径,而所做比较的次数恰为该结点在判定树上的层次数。

因此,折半查找成功时,关键字比较次数最多不超过判定树的深度。由于判定树的叶结点所在层次之差最多为 1,故 n 个结点的判定树的深度与 n 个结点的完全二叉树的深度相等,均为 [log2 n] +1。

这样,折半查找成功时,关键字比较次数最多不超过 [log2 n] +1。

相应地,折半查找失败时,对应判定树中从根结点到某个含空指针的结点的路径,因此,折半查找成功时,关键字比较次数最多也不超过判定树的深度 [log2 n] +1。

为便于讨论,假定表的长度 n=2(h次方)-1,则相应判定树必为深度是 h的满二叉树,h= log 2(n +1) 。又假设每个记录的查找概率相等,则折半查找成功时的平均查找长度为:
在这里插入图片描述
在这里插入图片描述
折半查找方法的优点是比较次数少,查找速度快,平均性能好;

缺点是要求待查表为有序表,且插入删除困难。因此,折半查找方法适用于不经常变动而查找频繁的有序列表。
在这里插入图片描述
例如对上述含 11 个记录的有序表,其折半查找过程可如上图的二叉判定树表示。二叉树中结点内的数值表示有序表中记录的序号,如二叉树的根结点表示有序表中第 6 个记录,图中的两条虚线分别表示上述查找关键字等于 12 的记录的过程,虚线经过的结点正是查找过程中和给定值比较过的记录,因此,记录在判定树上的“层次”恰为找到此记录时所需进行的比较次数。例如在长度为 11 的表中查找第 2 个记录时需要的比较次数为 4,因为该记录在判定树上位于第 4 层,查找过程中给定值先后和表中第 6、第 3、第 1 和第 2 个记录的关键字相比较。假设每个记录的查找概率相同,则从上图所示判定树可知,对长度为 11 的有序表进行折半查找的平均查找长度为

ASL=(1+2+2+3+3+3+3+4+4+4+4)/11 = 33/11 = 3

三、 分块查找法

分块查找法要求将列表组织成以下索引顺序结构:

  • 首先将列表分成若干个块(子表)。一般情况下,块的长度均匀,最后一块可以不满。每块中元素任意排列,即块内无序,但块与块之间有序。
  • 构造一个索引表。其中每个索引项对应一个块并记录每块的起始位置,以及每块中的最大关键字(或最小关键字)。索引表按关键字有序排列。

下图所示为一个索引顺序表。其中包括三个块,第一个块的起始地址为 0,块内最大关键字为 25;第二个块的起始地址为 5,块内最大关键字为 58;第三个块的起始地址为 10,块内最大关键字为88。

在这里插入图片描述
分块查找的基本过程如下:

  • ⑴ 首先,将待查关键字 K 与索引表中的关键字进行比较,以确定待查记录所在的块。具体的可用顺序查找法或折半查找法进行。
  • ⑵ 进一步用顺序查找法,在相应块内查找关键字为 K 的元素。
    例如,在上述索引顺序表中查找 36。首先,将 36 与索引表中的关键字进行比较,因为25<36≤58,所以 36 在第二个块中,进一步在第二个块中顺序查找,最后在 8 号单元中找到 36。

分块查找的平均查找长度由两部分构成,即查找索引表时的平均查找长度为 LB,以及在相应块内进行顺序查找的平均查找长度 LW。

在这里插入图片描述
假定将长度为 n 的表分成 b 块,且每块含 s 个元素,则 b=n/s。又假定表中每个元素的查找概率相等,则每个索引项的查找概率为 1/b,块中每个元素的查找概率为 1/s。若用顺序查找法确定待查元素所在的块,则有:
在这里插入图片描述
若用折半查找法确定待查元素所在的块,则有:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值