数据结构-06:静态查找
- 静态搜索表:集合中的结点总数是固定的或者很少发生变化。
- 动态搜索表:集合中的结点总数是经常在发生变化。
- 在内存中进行的搜索:重点减少比较、或查找的次数。评价标准:平均搜索长度。
- 在外存中进行的搜索:重点在于减少访问外存的次数。评价标准:读盘次数。
静态查找的前提是数据集合基本不变化,因此可以为了查找效率提前做一些组织工作,例如排序、建立索引或分块。选择查找算法时先看两个问题:数据是否有序,数据分布是否均匀。有序才能二分;分布均匀时插值查找才可能比二分更快;如果都不满足,顺序查找反而是最稳妥的基线方案。
顺序查找
1 | template <class KEY, class OTHER> |
哨兵 data[0].key = x 的作用是消除循环中的越界判断。循环从 size 向前找,只要真实数据中没有目标,最终一定会在 data[0] 停下。返回值为 0 时表示失败,返回正数时表示在对应位置找到。这个技巧牺牲了 0 号单元作为哨兵,使循环体更简洁。
- 时间复杂度为 ,其中 为数据元素的个数。
- 推导过程注意分为成功和不成功两部分,两种情况概率相等,同时每个结点搜索成功的概率也相等。
折半查找(二分查找)
1 | template <class KEY, class OTHER> |
二分查找的循环不变量是:如果目标存在,它一定在当前闭区间 [low, high] 内。每次比较 mid 后,算法都会排除掉一半不可能包含目标的区间。注意二分查找要求数据按关键字有序;如果数组无序,low 和 high 的移动就没有数学依据。
- 在最坏情况下,二分查找有序表的最大比较次数约为:
- 平均情况分析(只考虑查找成功的情况下):平均查找代价约为 。
- 平均情况分析(考虑成功、非成功查找两种的情况下):平均查找代价约为 。
- 时间复杂度为 。
插值查找
- 适用于数据分布比较均匀的情况,可以快速定位。
- 查找位置计算公式:
如果数组元素是 SET 类型,公式中的 data[low] 和 data[high] 应理解为对应的关键字 data[low].key 与 data[high].key。实际代码中还需要处理 data[high].key == data[low].key 的边界情况,避免除零。
- 缺点:计算查找位置比较复杂。
插值查找可以看作“按数值比例猜位置”的二分查找。二分总是去中点,而插值查找会根据 在 data[low] 和 data[high] 之间的相对位置估计下标。如果数据近似均匀分布,这个估计会很接近真实位置;如果数据分布极不均匀,估计位置可能反复偏向一侧,性能反而不稳定。
分块查找
- 它把整个有序表分成若干块,块内的数据元素可以是有序存储,也可以是无序的,但块之间必须是有序的。
- 查找由两个阶段组成:查找索引 (有序) 和查找块
分块查找是顺序查找和二分查找之间的折中。索引表记录每一块的最大关键字以及块的起始位置,先在索引表中找到目标可能属于哪一块,再在块内顺序查找。块越大,索引越短但块内扫描更慢;块越小,块内扫描更快但索引更长。






