首页/数据结构/06-search/查找算法对比 🔗 在 Obsidian 中打开
数据结构 · 06-search

查找算法对比

难度 ★★★重要度 ★★ 考查频率 低题型 选择 / 综合应用 数据结构/查找查找对比分析
速查
七类查找算法时间跨度极大:散列表 $O(1)$ 最快、折半 / B 树为 $O(\log n)$、分块 $O(\sqrt n)$、顺序 $O(n)$。选算法看数据是否有序、是否支持随机访问、是否频繁变动

速查

项目
主题查找算法对比
核心概念查找(Searching)是从数据集合中找出满足条件元素的过程
时间复杂度$O(1)$、$O(\log n)$、$O(\log_m n)$、$O(\sqrt n)$、$O(n)$
难度⭐⭐⭐
重要性⭐⭐

核心概念

查找(Searching)是从数据集合中找出满足条件的元素的过程。主要查找算法包括:

  1. 顺序查找
  2. 折半查找(二分查找)
  3. 分块查找
  4. 二叉搜索树(BST)查找
  5. 平衡二叉树(AVL)查找
  6. B 树 / B+ 树查找
  7. 散列表(哈希表)查找

关键性质

各算法对比总表

算法数据结构ASL(成功)ASL(失败)适用条件
顺序查找顺序表/链表(n+1)/2n无要求
折半查找有序顺序表$\approx\log_2(n+1)-1$$\approx\log_2(n+1)$有序 + 顺序存储
分块查找索引表+主表$\sqrt n+1$-块间有序
BST 查找二叉搜索树(n+1)/3 ~ nn+1
AVL 查找平衡二叉树$\approx1.44\log_2n$-平衡
B 树查找m 阶 B 树$O(\log_m n)$-多路平衡
散列查找散列表$O(1)$-散列函数

详细分析

1. 顺序查找(Sequential Search)

int SeqSearch(ElemType A[], int n, KeyType key) {
    for (int i = 0; i < n; i++)
        if (A[i] == key) return i;
    return -1;
}
  • 时间:$O(n)$
  • ASL成功:(n+1)/2
  • ASL失败:n
  • 优点:实现简单,对数据无要求
  • 缺点:效率低

带哨兵的顺序查找

int SeqSearch2(ElemType A[], int n, KeyType key) {
    A[0] = key;  // 哨兵
    int i = n;
    while (A[i] != key) i--;
    return i;  // 返回 0 表示失败
}

ASL成功:(n+1)/2(不变),但减少了一次比较判断。

2. 折半查找(Binary Search)

int BinarySearch(ElemType A[], int n, KeyType key) {
    int low = 0, high = n - 1;
    while (low <= high) {
        int mid = (low + high) / 2;
        if (A[mid] == key) return mid;
        else if (A[mid] < key) low = mid + 1;
        else high = mid - 1;
    }
    return -1;
}
  • 时间:$O(\log n)$
  • ASL成功:$\approx \log_2(n+1) - 1$
  • ASL失败:$\approx \log_2(n+1)$
  • 优点:效率高
  • 缺点:要求有序 + 顺序存储(不支持链表)
判定树n 个节点的折半查找判定树高度 = $\lfloor\log_2n\rfloor + 1$;左子树节点数比右子树多 0 或 1 个。

3. 分块查找

详见 分块查找

  • 时间:$O(\sqrt n)$
  • ASL:$\sqrt n + 1$(最优分块时)

4. 二叉搜索树(BST)

  • ASL成功最好:$O(\log n)$(平衡时)
  • ASL成功最坏:$O(n)$(退化为链表)
  • ASL失败:$O(n)$

5. 平衡二叉树(AVL)

  • ASL:$\approx 1.44\log_2n$
  • 保证:最坏情况也是 $O(\log n)$

6. B 树

  • m 阶 B 树:每个节点最多 m 棵子树
  • 查找时间:$O(\log_m n)$
  • 适用:磁盘等外部存储

7. 散列表(Hash Table)

  • ASL成功:取决于装填因子 $\alpha$,约为 $1 + \alpha/2$(链地址法)
  • ASL失败:约为 $1 + \alpha$(链地址法)
  • 理想情况:$O(1)$
  • 最坏情况:$O(n)$(所有元素冲突)

手算示例

示例一:比较各算法的 ASL

题目:1000 个元素,求各算法的成功 ASL。

算法ASL 计算结果
顺序查找(1000+1)/2500.5
折半查找$\log_2(1001)-1$$\approx9$
分块查找$\sqrt{1000}+1$$\approx32.6$
AVL 树$1.44\times\log_21000$$\approx14.4$
散列表取决于 $\alpha$$\approx O(1)$

示例二:折半查找的判定树

题目:对有序表 (1,3,5,7,9,11,13,15) 构造折半查找判定树。

第一次:$mid=(0+7)/2=3$,$A[3]=7$;左半:(0,2),右半:(4,7)。

左半:$mid=(0+2)/2=1$,$A[1]=3$;左左:(0,0),左右:(2,2)。

右半:$mid=(4+7)/2=5$,$A[5]=11$;右左:(4,4),右右:(6,7)。

右右:$mid=(6+7)/2=6$,$A[6]=13$;右右左:无,右右右:(7,7)。

         7
       /   \
     3      11
    / \    /  \
   1   5  9    13
                 \
                  15

查找成功 ASL = $(1\times1 + 2\times2 + 3\times4 + 4\times1) / 8 = (1+4+12+4)/8 = \frac{21}{8} = 2.625$

查找失败 ASL = 考虑外部节点(空指针)。

示例三:散列表的 ASL

题目:有 10 个元素,散列表大小 13,用链地址法,装填因子 $\alpha=10/13$。

ASL成功 $\approx 1 + \alpha/2 = 1 + \frac{10}{26} \approx 1.38$

ASL失败 $\approx 1 + \alpha = 1 + \frac{10}{13} \approx 1.77$

常见考法

题型一:ASL 计算给定数据和查找方法,计算成功 / 失败的平均查找长度。
题型二:选择最优算法给定场景,选择最合适的查找算法。
题型三:折半查找判定树构造判定树,计算 ASL。
题型四:散列表冲突处理用开放定址法或链地址法构造散列表,计算 ASL。

易错点

注意
  1. 折半查找不能用于链表:因为需要随机访问 mid 位置。
  2. 折半查找要求顺序存储 + 有序:两个条件缺一不可。
  3. 散列表的 $O(1)$ 是平均情况:最坏情况可能是 $O(n)$。
  4. BST 的 ASL 取决于树的形态:最好 $O(\log n)$,最坏 $O(n)$。
  5. 分块查找不需要完全有序:只需要块间有序。

核心结论

场景推荐算法原因
数据量小顺序查找实现简单
有序数组折半查找$O(\log n)$
频繁插入删除BST / AVL / 散列表动态结构
外部存储B 树 / B+ 树减少磁盘 I/O
追求 $O(1)$散列表最快

记忆卡片

折半查找的前提条件是什么?
有序 + 顺序存储(支持随机访问)。链表不能用折半查找。
1000 个元素用折半查找,最多比较多少次?
$\lfloor\log_21000\rfloor + 1 = 10$ 次(判定树的高度)。
散列表的理想时间复杂度?最坏呢?
理想 $O(1)$,最坏 $O(n)$(所有元素都冲突时退化为顺序查找)。
分块查找与折半查找的 ASL 差多少?
分块 $O(\sqrt n)$ 远大于折半 $O(\log n)$。例如 $n=1000$,分块约 32,折半约 10。
什么情况下顺序查找反而比折半好?
链表存储时(折半不能用于链表),或数据量很小时(顺序实现简单,常数因子小)。
BST 查找的最坏 ASL 是多少?
退化为链表时 $O(n)$;平衡(AVL)时保证 ≈ $1.44\log_2n$。

交互动画 · n=1000 时成功 ASL 对比

横条长度按对数刻度示意;数值标注为真实 ASL 顺序查找500.5 分块查找≈32.6 AVL 树≈14.4 折半查找≈9 散列表≈1.38 散列表最快,顺序查找最慢;折半 / 分块 / AVL 居中。可见数据结构约束对效率影响巨大。
点击「播放对比」依次查看各算法的成功 ASL
n = 1000 个元素

相关知识点

(暂无关联知识点)—— 本卡片为 06-search 章节的总结对比,未设置关联链接。