[算法笔记]线性表的查找

线性表的查找

脑图1

分三种:

  • 顺序查找法
  • 折半查找法
  • 分块查找法

顺序查找

其实就是从表的一端向另一端遍历,依次比较关键字。

线性表的顺序查找嘛,具体一点我们就可以认为是按下标逐项遍历数组。那么对于返回值我们可以要求查找成功就返回其下标,未查找成功返回一个约定值(约定值显然不应当出现在数据项可能的取值中)。

实现又分两种,即有无哨兵。哨兵安放在头或尾部。

DataSource[0] = key;  //头部监视哨兵
int i = len;          //len记录了数据项的数量
while(DataSource[i] != key) {
	i--;
}
return i;
//显然返回的若是0则没查找到,非零则查找到

尾部哨兵也类似,就是从头开始遍历。

折半查找

前提是要查找的数据项有序排列

二分法嘛。

while(low <= high){
	mid = (low + high) / 2;
	if(k == DataSource[mid]){
		//查找到的情况
		return mid;
	}
	else if(key < DataSource[mid]){
		high = mid - 1;
	}
	else{
		low = mid + 1;
	}
}
//存在遍历完都没找到的情况
return -1;

这里啊不管是移动high还是low都是越过mid的,比如说low = mid + 1而不是low - mid。虽然这个很显而易见,但是还是提醒注意一下。

总而言之就是看mid的比较结果。

折半查找对于适合顺序存储结构(效率高于链表)

对于上述结论结合链表的遍历考虑一下就很明显了,显然数组更适合随机存取

折半算法时间复杂度是 O ( l o g 2 n ) O(log_2n) O(log2n)

{ C ( 1 ) = 1 C ( n ) = C ( n / 2 ) + 1 \left\{ \begin{aligned} C(1)=1\\ C(n)=C(n/2)+1 \end{aligned} \right. {C(1)=1C(n)=C(n/2)+1

由上述递推式得到复杂度是 O ( l o g 2 n ) O(log_2n) O(log2n),过程如下:

C ( n ) = C ( n / 2 ) + 1 C ( n ) = C ( n / 4 ) + 1 + 1 . . . C ( n ) = C ( n / 2 k ) + 1 ∗ k \begin{aligned} C(n)=C(n/2)+1 \\ C(n)=C(n/4)+1+1 \\ ... \quad \quad \quad \quad \\ C(n)=C(n/{2^k})+1*k \end{aligned} C(n)=C(n/2)+1C(n)=C(n/4)+1+1...C(n)=C(n/2k)+1k

进一步对最后一项式子代入 k = log ⁡ 2 n k=\log_2 n k=log2n

C ( n ) = C ( 1 ) + 1 ∗ log ⁡ 2 n = 1 + log ⁡ 2 n C(n)=C(1)+1*\log_2 n=1+\log_2 n C(n)=C(1)+1log2n=1+log2n

忽略常数项,得到 log ⁡ 2 n \log_2 n log2n

判定树/比较树

判定树
图源自《数据结构教程》,是对0~10这个十一个有序元素的折半查找的比较树。

这种树用于描述查找过程。显然层数代表比较次数(例如查找2或者8需要比较2次,因为它们都处在第二层)

  • 内部结点:判定树中查找成功的结点
  • 外部结点:判定树中查找失败对应的结点

说白了就是上图里圆形的是内部结点,方形的是外部结点。显然,在构造比较树时内部结点(或者说数据项)不作为叶子结点。

分块查找法

性能是介于顺序查找和折半查找之间的

整个数据集均分b块,前b-1块必须满的,最后一块可以不满。唯一的要求是前一块最大的关键字小于后一块的最小关键字

特点是:块内无序,块间有序

然后查找思路就是对索引表进行二分查找并找到元素可能在哪个块,找到可能在哪个块后再于该块内进行顺序查找

索引存储结构
索引表保存的是最大关键字,然后找到可能所在的区块进行顺序查找(例如上图如果查找77,由于 66 < 77 < 85 66 < 77 < 85 66<77<85,所以在最大值是85那个块里进行顺序查找)

索引表是在存储数据的同时可以附加建立的一种方便查找的表。一般形式为(关键字,地址),关键字就是数据表的某个元素的数据项,地址可以是指向该元素的指针,或者相对地址(比如数组下标)。

对于总数据项n的数据表每块最佳元素个数是 n \sqrt n n

因为这个时候 A S L ASL ASL才取到极小值

三种方法的ASL分析

A S L ASL ASL分为两类,即

  • A S L s u c c e s s ASL_{success} ASLsuccess即查找成功情况下的 A S L ASL ASL
  • A S L f a i l u r e ASL_{failure} ASLfailure即查找失败情况下的 A S L ASL ASL

它的公式是这样的:

A S L = ∑ i = 1 n p i c i ASL=\sum_{i=1}^np_ic_i ASL=i=1npici

其中:

  • p i p_i pi为查找第 i i i个元素的概率
  • c i c_i ci为找到地 i i i个元素所需的关键字比较次数

A S L ASL ASL是衡量查找算法性能好坏的重要指标, A S L ASL ASL越小则对应的查找算法的性能越好


我认为: p i p_i pi理解为于第 i i i项结束查找的概率比较合适。

我这么说的原因是更方便与解释 A S L f a i l u r e ASL_{failure} ASLfailure的问题。愚以为书上面的定义(上面定义就是原封不动照搬书上的)有点笼统,没说清楚。

顺序查找

A S L s u c c e s s = ∑ i = 1 n p i c i = 1 n ∑ i = 1 n i = 1 n × n ( n + 1 ) 2 = n + 1 2 ASL_{success} = \sum_{i=1}^{n}p_ic_i=\frac1n \sum_{i=1}^{n}i = \frac1n ×\frac{n(n+1)}{2}=\frac{n+1}2 ASLsuccess=i=1npici=n1i=1ni=n1×2n(n+1)=2n+1

对上式需要解释的是 p i = 1 n p_i = \frac 1n pi=n1被直接提到求和前面,因为不管 i i i取几 p i p_i pi是不变的。这是因为每个元素是关键字的可能性是相同的(都是 1 n \frac 1n n1)。

A S L f a i l u r e = n ASL_{failure}=n ASLfailure=n

对上式的解释是: A S L ASL ASL代表平均需要进行的和关键值比较的操作的次数。显然顺序查找失败意味着整个数据表都遍历了一遍并且没有找到,整个数据表有 n n n项数据,则 A S L f a i l u r e = n ASL_{failure}=n ASLfailure=n是显然的。

或者说 p 1 = p 2 = . . . = p n − 1 = 0 p_1 =p_2=... =p_{n-1}=0 p1=p2=...=pn1=0,只有 p n = 1 p_n=1 pn=1,并且又由于 c n = n c_n=n cn=n,可以得到上述结果。

对比看来,在关键字确实出现在n项数据中时,第 i i i项数据结束查找的可能性的确是 1 n \frac 1n n1(等可能地分布),但是放到关键字不出现在n项数据中的情况下,比较 n n n次的是一定的,所以比较1、2、… n − 1 n-1 n1次结束查找的概率必然都为0。

折半查找

A S L s u c c e s s = ∑ i = 1 n p i × l e v e l ( k i ) ASL_{success} = \sum_{i=1}^{n}p_i×level(k_i) ASLsuccess=i=1npi×level(ki)

其中 l e v e l ( k i ) level(k_i) level(ki)表示关键字 k i k_i ki对应内部结点(在比较树中)的层次。

这个可以进一步简化为

A S L s u c c e s s = 1 n ∑ i = 1 n 2 i − 1 × i = n + 1 n × log ⁡ 2 ( n + 1 ) − 1 ≈ log ⁡ 2 ( n + 1 ) − 1 ASL_{success} =\frac 1n \sum_{i=1}^{n}2^{i-1}×i= \frac{n+1}n ×\log_2(n+1)-1 \approx \log_2(n+1)-1 ASLsuccess=n1i=1n2i1×i=nn+1×log2(n+1)1log2(n+1)1

这个近似结果前提是判定树近似为高 h = log ⁡ 2 ( n + 1 ) h=\log _2 (n+1) h=log2(n+1)的满二叉树(内部结点有n个,高度h不计外部结点)。


A S L f a i l u r e = ∑ i = 0 n q i × ( l e v e l ( u i ) − 1 ) ASL_{failure}=\sum_{i=0}^{n}q_i×(level(u_i)-1) ASLfailure=i=0nqi×(level(ui)1)

因为对于一个具有 n n n个数据项的查找树,其外部结点必然有 n + 1 n+1 n+1个,这意味着有 n + 1 n+1 n+1中查找失败的情况。

在这里插入图片描述
对于上图,有:

A S L s u c c e s s = 1 ∗ 1 + 2 ∗ 2 + 4 ∗ 3 + 4 ∗ 4 11 ASL_{success} = \frac{1*1 + 2*2+4*3+4*4}{11} ASLsuccess=1111+22+43+44

A S L f a i l u r e = 4 ∗ 3 + 8 ∗ 4 12 = 3.67 ASL_{failure}=\frac{4*3+8*4}{12}=3.67 ASLfailure=1243+84=3.67

比如说这个 A S L f a i l u r e ASL_{failure} ASLfailure的分子意思就是第三层(比较3次)查找失败的节点有4个(写作3*4),第四层(比较4次)查找失败的节点有8个(写作4*8

分块查找

A S L b l k = A S L b n + A S L s q ASL_{blk}=ASL_{bn}+ASL_{sq} ASLblk=ASLbn+ASLsq

其中

  • A S L b n ASL_{bn} ASLbn是对索引表查找
  • A S L s q ASL_{sq} ASLsq是对对应块的顺序查找

对于一个数据表有n个元素,块大小为s时,共有 b = c e i l ( n / s ) b=ceil(n/s) b=ceil(n/s)个块。套用上面的结论

A S L b l k = log ⁡ 2 ( b + 1 ) − 1 + s + 1 2 ≈ log ⁡ 2 ( b + 1 ) + s 2 ASL_{blk}=\log_2(b+1) -1 + \frac{s+1}{2} \approx \log_2(b+1) + \frac{s}{2} ASLblk=log2(b+1)1+2s+1log2(b+1)+2s

参考

李春葆《数据结构教程》
关于ASL(平均查找长度)的简单总结
轻松学习数据结构(代码完整!)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值