查找算法涉及两个主要问题:一是数据如何组织—— 查找表;而是在查找表上如何查找—— 查找方法。
查找过程中往往是依据数据元素的某个数据项进行 的,这个数据项通常是数据的关键字。可以唯一确定一个数据元素的关键字称为主关键字,不能唯一确定一个数据元素的关键字称为次关键字。常用的查找方法主要有顺序查找、索引查找和哈希查找。
ASL的定义
在进行查找性能的分析时。经常采用平均查找长度(Average Search Length,ASL)来衡量查找算法的性能优劣。
ASL定义为查找过程中,给定值与数据关键字比较次数的期望值。对于具有n个记录的顺序表,查找成功时的平均查找长度(ASL)为
ASL= ∑ i = 1 n \sum_{i=1}^n ∑i=1n P i P_i Pi C i C_i Ci
其中, P i P_i Pi为查找第i个记录的概率; C i C_i Ci为找到第i个记录数据需要比较的次数, C i C_i Ci随查找过程的不同而不同。
顺序查找
依次查找表中的数据,直到找到或者遍历完之后仍未找到为止。
基本思想及查找算法
从表中指定位置(一般为末尾)开始,沿某个方向记录的关键字与给定的关键字的值进行比较,若某个记录的关键字和给定的关键字值相等,则查找成功;反之,若找完整个顺序表仍未找到,则查找失败。
通常情况下,我们可以将表头(第0个位置)设置为岗哨,即对R[0]赋值key,目的是免去查找过程中每次比较关键字都要判断是否到达表头(只需要在R[i]=key时判断i是否等于0),一定程度上减少了查找时间。
顺序表的顺序查找算法如下
typedef int DataType;
typedef int KeyType;//DataType和KeyType可根据使用需求修改
typedef struct SeqList
{
DataType key;
}SeqList;
int seqsearch(SeqList S[], int n, KeyType key)
{
int i = n;
S[0].key = key;
while (S[i].key != key)i--;
return i;
}
查找性能
空间复杂度:对于顺序查找,一般只需要一个辅助存储单元空间,因此空间复杂度为O(1);
时间复杂度:平均情况下,需要和顺序表中大约一半的记录进行比较,即比较次数为
n
2
\frac{n}{2}
2n,时间复杂度为O(n)。
若每个记录的查找概率相等,即
P
i
P_i
Pi=
1
n
\frac{1}{n}
n1
则等概率情况下顺序查找的平均查找长度为
A S L s ASL_s ASLs = ∑ i = 1 n \sum_{i=1}^n ∑i=1n P i P_i Pi C i C_i Ci = 1 2 \frac{1}{2} 21 ∑ i = 1 n \sum_{i=1}^n ∑i=1n (n-i+1) = n + 1 2 \frac{n+1}{2} 2n+1
若考虑查找失败的情况,平均查找长度就是查找成功和不成功的平均查找长度之和。查找不成功时的比较次数为顺序表的长度。假设查找成功和不成功的可能性相同,对每条记录的查找概率也相等,则平均查找长度为
ASL= n + n + 1 2 2 \frac{n+\frac{n+1}{2}}{2} 2n+2n+1 = 3 n + 1 4 \frac{3n+1}{4} 43n+1
索引查找
索引表的构建
- 按表中数据的关键字将表分成若干块: R 1 R_1 R1, R 2 R_2 R2,… R L R_L RL,这些块要满足第 R k R_k Rk块中所有关键字<=Rk+1块中所有关键字,k=1,2,…,L-1,称为“分块有序”。也可以满足第Rk块中所有关键字 >=Rk+1块中所有关键字,此时,分块降序排序。
- 对每块建立一个索引项,每一个索引项包含两项内容:
A. 关键字项,为该块中最大关键字值。
B. 指针项,为该块第一个记录在表中的位置。 - 索引表的查找。
索引表的查找分两步进行:
(1) 查找目录。将外存上含有索引区的页块调入内存,根据索引表的关键字项查找记录所在块,根据其指针项确定所在块的第一个记录的物理地址。
(2) 查找数据。将含有该数据的页块调入内存,在这个块内部根据关键字查找记录的详细信息。
索引表的顺序查找算法
typedef int DataType;
typedef int KeyType;//DataType和KeyType可根据使用需求修改
typedef struct IndexType
{
KeyType key;//关键字项
int Link;//指针项
}IndexType;
typedef struct SeqList
{
DataType key;
}SeqList;
int IndexSeqSearch(IndexType Ls[], SeqList S[], int m, int n, KeyType key)
{
//索引表Ls中顺序查找关键字为key,索引表长度为m
//顺序表为R,块长为n
int i = 1;
while (i <= m && key > Ls[i].key)i++;//块间查找
if (i > m)return -1;//查找失败
else
{
//块内顺序查找
int j = Ls[i].Link;
while (key != S[j].key && j - Ls[i].Link < 2)j++;
if (key == S[j].key)return j;//成功
else return -1;//失败
}
}
查找性能
索引块间和索引块内部都采用顺序查找,则查找的平均查找长度为块间及块内平均查找长度之和,即
ASL = Lb + Lw = 1 b \frac{1}{b} b1 ∑ j = 1 b \sum_{j=1}^b ∑j=1b j + 1 s \frac{1}{s} s1 ∑ i = 1 s \sum_{i=1}^s ∑i=1s i = 1 2 \frac{1}{2} 21 ( m s \frac{m}{s} sm + s) + 1
其中,m为表长,均匀分为b块,每块含有s个记录。
由上可知,索引表顺序查找的时间复杂度不仅和表长m有关,而且和每一块中的记录个数s有关。为了提高查找效率,在m确定的情况下,应选择合适的s。当s取 n \sqrt{n} n时,ASLbs取最小值 n + 1 \sqrt{n+1} n+1.