作为数据结构的课程笔记,以便查阅。如有出错的地方,还请多多指正!
目录
- 查找表 (Search Table):由同一类型的数据元素(或记录)构成的集合
- 静态查找表 (Static Search Table):仅作查询和检索操作的查找表
- 动态查找表 (Dynamic Search Table)
查找算法的时间复杂度
- 查找算法的 基本运算 为 被查找元素
x
x
x 与查找表中元素的比较。因此,查找算法的 时间复杂度 即为 比较次数,平均情况下的时间复杂度
A
(
n
)
A(n)
A(n) 可由 平均查找长度 ASL (Average Search Length) 来衡量
- ASL:为确定记录在表中的位置,需要与给定值进行比较的次数的期望值
- 对含有 n n n 个记录的表, 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 个元素所需比较次数
顺序查找 Sequential Search
查找过程
- 查找过程:从表的一端开始逐个进行记录的关键字和给定值的比较
- 适用条件:以顺序表或线性链表表示的静态查找表
算法实现
//顺序查找
int Search_seq(SSTable_t* sstable, Key_t key)
{
int i;
sstable->rec[0].key = key;
for (i = sstable->len; sstable->rec[i].key != key; --i)
{
}
return i;
}
- 第 0 个元素起监视哨的作用。这个改进能使顺序查找在 n ≥ 1000 n\geq1000 n≥1000 时,进行一次查找的平均时间几乎减半
性能分析
ASL
- 表中各元素查找概率相等时
A S L = ∑ i = 1 n p i c i = 1 n ∑ i = 1 n ( n − i + 1 ) = n + 1 2 ASL=\sum_{i=1}^np_ic_i=\frac{1}{n}\sum_{i=1}^n(n-i+1)=\frac{n+1}{2} ASL=i=1∑npici=n1i=1∑n(n−i+1)=2n+1 - 表中各元素查找概率不等时
A S L 在 p n ≥ p n − 1 ≥ . . . ≥ p 2 ≥ p 1 时 取 m i n ASL在p_n\geq p_{n-1} \geq...\geq p_2\geq p_1时取min ASL在pn≥pn−1≥...≥p2≥p1时取min
因此,若能预知每个记录的查找概率,则应先对记录按查找概率进行升序排序。若查找概率无法事先测定,则可以为记录增设访问频度域,始终保持按访问频度非递减有序排列,使查找概率大的记录不断后移,或每次查找之后将刚查找到的记录直接移至表尾 - 当查找不成功的情形不能忽视时,假设要查找的元素
x
x
x 在
L
L
L 中概率是
p
p
p, 且每个位置概率相等,则
A ( n ) = A S L = ∑ i = 1 n i p n + ( 1 − p ) n = p ( n + 1 ) 2 + ( 1 − p ) n A(n)=ASL=\sum_{i=1}^ni\frac{p}{n}+(1-p)n=\frac{p(n+1)}{2}+(1-p)n A(n)=ASL=i=1∑ninp+(1−p)n=2p(n+1)+(1−p)n当 p = 1 2 p=\frac{1}{2} p=21 时, A ( n ) = A S L = 3 n + 1 4 ≈ 3 n 4 A(n)=ASL=\frac{3n+1}{4}\approx\frac{3n}{4} A(n)=ASL=43n+1≈43n
总结
- 优点:算法简单,对表的逻辑次序和存储结构无要求
- 缺点:平均查找长度较大
折半查找 Binary Search
查找过程
- 查找过程:二分法
- 适用条件:采用顺序存储结构的有序静态查找表
算法实现
- 需要注意的是,如果二分上界超过
int
型数据范围的一半,那么当欲查询元素在序列较靠后的位置时, 语句mid = (left + right) / 2
中的left + right
就有可能超过int
而导致溢出, 此时一般使用mid = left + (right - left) / 2
这条等价语句作为代替以避免溢出
int Binary_search(SSTable_t* sstable, Key_t key)
{
int low = 1;
int high = sstable->len;
int mid = (low + high) / 2;
while (low <= high)
{
if (sstable->rec[mid].key < key)
{
low = mid + 1;
}
else if (sstable->rec[mid].key > key)
{
high = mid - 1;
}
else {
return mid;
}
mid = (low + high) / 2;
}
return 0;
}
性能分析
判定树
- 判定树:描述查找过程的二叉树。判定树并非完全二叉树,但它的叶结点所在层数之差最多为 1,深度与同样结点数的完全二叉树相同。因此 n n n 个结点的判定树深度为 ⌊ l o g 2 n ⌋ + 1 \lfloor log_2n \rfloor +1 ⌊log2n⌋+1
- 折半查找法的比较次数不超过其判定树的深度,因此折半查找在查找成功时最多进行
⌊
l
o
g
2
n
⌋
+
1
\lfloor log_2n \rfloor +1
⌊log2n⌋+1 次关键字比较; 查找不成功时的比较次数为判定树深度 +1
ASL
- 设判定树为深度为
h
h
h 的满二叉树
A S L = ∑ i = 1 n p i c i = 1 n ∑ j = 1 h j ⋅ 2 j − 1 = n + 1 n l o g 2 ( n + 1 ) − 1 ≈ l o g 2 ( n + 1 ) − 1 ASL=\sum_{i=1}^np_ic_i=\frac{1}{n}\sum_{j=1}^hj\cdot 2^{j-1}=\frac{n+1}{n}log_2(n+1)-1\approx log_2(n+1)-1 ASL=i=1∑npici=n1j=1∑hj⋅2j−1=nn+1log2(n+1)−1≈log2(n+1)−1 - 在不等概率查找的情况下,折半查找不是有序表查找的最好方法。应使查找概率大的元素更接近判定树的根
总结
- 优点:平均查找长度较小
- 缺点:只适用于顺序存储的有序表,不适用于一般顺序表和链式存储结构
证明在每个元素的搜索概率相等时,二分查找效率最高
- 设有
N
N
N 个数,一次分割查找后剩下数越少效率越高。设分割比为
x
x
x,
1
−
x
1-x
1−x,分完后剩
n
n
n 个数, 则
P
(
n
=
N
x
)
=
x
,
P
(
n
=
N
(
1
−
x
)
)
=
1
−
x
P(n=Nx)=x,P(n=N(1-x))=1-x
P(n=Nx)=x,P(n=N(1−x))=1−x
∴ E ( n ) = N x ⋅ x + N ( 1 − x ) ⋅ ( 1 − x ) = N ( x 2 + ( 1 − x ) 2 ) \therefore E(n)=Nx\cdot x + N(1-x)\cdot (1-x) = N(x^2+(1-x)^2) ∴E(n)=Nx⋅x+N(1−x)⋅(1−x)=N(x2+(1−x)2)上式在 x = 0.5 x=0.5 x=0.5 时最小,即二分法查找效率最高
分块查找 / 索引顺序查找
查找过程
- 查找过程:将线性表分成几块,块内无序,块间有序;先确定待查记录所在块,再在块内查找
- 适用条件:分块有序表
算法实现
- 建立索引表,每个索引表结点含有一个数据域(本块最大关键字)和一个指针域(指向本块第一个结点)
- 索引表查找可用顺序、折半查找
- 块内查找只能用顺序查找
- 数据可用链式存储
性能分析
ASL
- 设
L
b
L_b
Lb 为查找索引表确定所在块的平均查找长度,
L
w
L_w
Lw 为在块中查找元素的平均查找长度
A S L = L b + L w ASL=L_b+L_w ASL=Lb+Lw将表长为 n n n 的表平均分成 b b b 块,每块含 s s s 个记录,并设表中每个记录的查找概率相等,则 - 用顺序查找确定所在块
A S L = b + 1 2 + s + 1 2 ASL=\frac{b+1}{2}+\frac{s+1}{2} ASL=2b+1+2s+1 - 用折半查找确定所在块
A S L ≈ l o g 2 ( b + 1 ) + s − 1 2 ASL\approx log_2(b+1)+\frac{s-1}{2} ASL≈log2(b+1)+2s−1