数据结构——查找

查找

一、顺序查找

1. 基本思想

顺序查找(Sequential Search)是最简单、最直观的查找方法。从线性表的一端开始,逐个将每个元素的关键字与给定值进行比较,若相等则查找成功;若遍历完整个表仍未找到,则查找失败。

顺序查找对表没有任何要求——无论表是否有序、无论采用顺序存储还是链式存储,都可以使用。

2. 代码实现

int SeqSearch(int a[], int n, int key) {
    for (int i = 0; i < n; i++) {
        if (a[i] == key)
            return i;       // 查找成功,返回下标
    }
    return -1;              // 查找失败
}
  • 时间复杂度:$O(n)$
  • 空间复杂度:$O(1)$,只需几个临时变量

3. 优缺点

优点:

  • 对表的结构无要求——无序、有序、顺序表、链表均可
  • 实现简单,适合数据量较小的场景

缺点:

  • 查找效率低,当 n 较大时不适合
  • 每次比较都是线性遍历,无法利用顺序信息

二、折半查找

1. 基本思想

折半查找(Binary Search,又称二分查找)是一种在有序线性表中进行查找的高效算法。其核心思想是每次将待查找区间一分为二,取中间位置的元素与关键字比较:

  • 若相等,则查找成功
  • 若中间元素大于关键字,则在左半部分继续查找
  • 若中间元素小于关键字,则在右半部分继续查找

重复上述过程,直到找到或区间为空。

类比生活:查英文词典时,要查一个单词绝不会从头翻到尾——而是根据字母顺序直接翻到大致位置,然后根据页眉的单词范围决定往前翻还是往后翻。这就是折半查找的思路。

2. 前提条件

折半查找的使用有两个严格前提:

  1. 线性表必须采用顺序存储结构(数组),因为需要随机存取中间位置的元素
  2. 表中的元素必须按关键字有序排列(通常为升序)

链表无法使用折半查找,因为链表不支持随机存取,无法在 $O(1)$ 时间内获取中间位置的元素。

3. 代码实现

int BinarySearch(int a[], int n, int key) {
    int low = 0, high = n - 1, mid;
    while (low <= high) {
        mid = low + (high - low) / 2;   // 取中间位置,防止 (low+high) 溢出
        if (a[mid] == key)
            return mid;                 // 查找成功
        else if (a[mid] > key)
            high = mid - 1;             // 在左半部分继续查找
        else
            low = mid + 1;              // 在右半部分继续查找
    }
    return -1;                          // 查找失败
}

注意mid = (low + high) / 2lowhigh 都很大时可能溢出,写成 low + (high - low) / 2 可以避免此问题。

  • 时间复杂度:$O(\log_2 n)$
  • 空间复杂度:$O(1)$(非递归实现)

当 n 较大时,折半查找的效率远高于顺序查找。但折半查找要求表必须有序,且只能用于顺序表,这两个限制在某些场景下并不满足。


三、索引查找

1. 基本思想

索引查找(Index Search,又称分块查找,Block Search)是顺序查找的一种改进。它将查找表分为若干(Block),并额外建立一个索引表来管理这些块。

核心思想可以概括为:"块间有序,块内无序"

具体来说:

  • 将长度为 n 的表均匀分为 b 块,每块有 s 个元素(最后一块可能少于 s 个)
  • 块内元素可以任意排列,没有顺序要求
  • 块间是有序的——第 i 块中的所有元素都小于第 i+1 块中的所有元素
  • 建立一个索引表,每个索引项包含该块的最大关键字起始地址

2. 查找过程

索引查找分两步进行:

  1. 在索引表中确定待查关键字所在的块——索引表是按块间顺序排列的,可用顺序查找或折半查找
  2. 在块内进行顺序查找——块内元素无序,只能用顺序查找

3. 代码实现

// 索引表结构
typedef struct {
    int maxKey;    // 块中最大的关键字
    int start;     // 块的起始下标
    int length;    // 块的长度
} IndexItem;

// 分块查找
int BlockSearch(int a[], int n, IndexItem index[], int m, int key) {
    // 第一步:在索引表中确定所在块(顺序查找)
    int i = 0;
    while (i < m && index[i].maxKey < key)
        i++;
    if (i >= m) return -1;  // 查找失败,所有块的最大关键字都小于 key
    
    // 第二步:在对应块内进行顺序查找
    for (int j = index[i].start; j < index[i].start + index[i].length; j++) {
        if (a[j] == key)
            return j;
    }
    return -1;              // 块内未找到
}

上例中索引表用顺序查找定位块。如果块数较多,也可在索引表上使用折半查找进一步提高效率。

时间复杂度:$O(\sqrt{n})$(均衡分块时)

4. 优缺点及适用场景

优点:

  • 插入和删除操作方便——只需在对应块内操作,不需要移动大量元素
  • 不需要整个表有序,仅需块间有序,建表成本低于全排序
  • 性能介于顺序查找和折半查找之间

缺点:

  • 需要额外的存储空间存放索引表
  • 需要对表进行分块和排序预处理
  • 查找效率不如折半查找

适用场景:当数据量较大、数据经常动态插入和删除(不适合折半查找)、又希望比顺序查找更快时,索引查找是一个折中的好选择。


四、三种查找方法对比

对比项 顺序查找 折半查找 索引查找
数据结构要求 有序顺序表 块间有序
存储结构 顺序表或链表 仅限顺序表 顺序表
时间复杂度 $O(n)$ $O(\log_2 n)$ $O(\sqrt{n})$
优点 实现最简单,适用范围广 效率极高 插入删除方便,效率适中
缺点 效率低 仅适用于有序顺序表 需额外索引空间

选择建议

  • 无序或使用链表存储 → 顺序查找
  • 有序且采用顺序存储、查找频繁 → 折半查找
  • 数据量较大、插入删除频繁、又想有一定的查找效率 → 索引查找
posted @ 2026-07-16 15:35  Javenwww  阅读(16)  评论(0)    收藏  举报