本篇文章主要介绍线性表中的折半查找与分块查找算法。
线性表的查找:
1,查找的基本概念及顺序查找
2,折半查找
折半查找:每次将待查记录所在区间缩小一半。(有序表表示静态查找表)
下面给出一个例子分析折半查找的过程,一共用到了两个指针high和low,mid始终等于(high+low)/2。
若k==R[mid].key,查找成功;若k<R[mid].key,则high=mid-1;若k>R[mid].key,则low=mid+1
若k==R[mid].key,查找成功
若k<R[mid].key,则high=mid-1
若k>R[mid].key,则low=mid+1
直至low>high时,查找失败
由上述实例我们可以总结出折半查找的步骤:
以下是代码实例
1. 初始化两个变量`low`和`high`,分别表示查找范围的下界和上界。`low`初始化为1,因为数组索引从1开始;`high`初始化为ST.length,即表的长度。
2. 使用`while`循环进行二分查找,条件是`low <= high`。
3. 计算中间位置`mid`,方法是取`low`和`high`的平均值。
4. 判断中间位置的元素`ST.R[mid].key`是否等于待查找的关键字`key`。如果相等,说明找到了目标元素,返回`mid`。
5. 如果`key`小于`ST.R[mid].key`,说明目标元素在前半部分,更新`high`为`mid - 1`。
6. 如果`key`大于`ST.R[mid].key`,说明目标元素在后半部分,更新`low`为`mid + 1`。
7. 当`low > high`时,说明查找范围为空,即没有找到目标元素,返回0。
int Search_Bin(SSTable ST,KeyType key)
{
//若找到,则函数值为该元素在表中的位置,否则为0
low=1;high=ST.length; while(low<=high){
mid=(low+high)/2;
if(key==ST.R[mid].key) return mid;
else if(key<ST.R[mid].key) high=mid-1; //前一子表查找
else low=mid+1; //后一子表查找
} return 0; //表中不存在待查元素
}
下面是使用递归算法
函数接收四个参数:一个SSTable对象ST,一个要查找的关键字key,以及查找范围的下界low和上界high。如果找到了关键字,函数返回该元素在表中的位置;如果没有找到,函数返回0。
1. 首先判断查找范围是否有效,即low是否大于high。如果是,说明查找范围为空,返回0表示查找不到目标元素。
2. 计算中间位置mid,方法是取low和high的平均值。
3. 判断中间位置的元素ST.elem[mid].key是否等于待查找的关键字key。如果相等,说明找到了目标元素,返回mid。
4. 如果key小于ST.elem[mid].key,说明目标元素在前半部分,递归调用Search_Bin函数,将查找范围更新为low到mid-1。
5. 如果key大于ST.elem[mid].key,说明目标元素在后半部分,递归调用Search_Bin函数,将查找范围更新为mid+1到high。
这段代码中的省略号(……)表示需要补充递归调用的部分。
int Search_Bin (SSTable ST, keyType key, int low, int high)
{
if(low>high) return 0; //查找不到时返回0
mid=(low+high)/2;
if(key等于ST.elem[mid].key) return mid;
else if(key小于ST.elem[mid].key)
……..//递归
else……. //递归
}
实际上我们可以使用二叉判定树来理解折半查找的过程
比较的次数<=树的深度
下面是一个练习
折半查找的性能分析:
我们可以总结出折半查找的特点:
3,分块查找
分块查找的特点是分块有序,块内无序。分块有序,即分成若干子表,要求每个子表中的数值都比后一块中数值小(但子表内部未必有序)。然后将各子表中的最大关键字构成一个索引表,表中还要包含每个子表的起始地址(即头指针)。我们通过下面这个图来理解。
分块查找的过程:
① 对索引表使用折半查找法(因为索引表是有序表);
② 确定了待查关键字所在的子表后,在子表内采用顺序查找法(因为各子表内部是无序表);
下面是一个分块查找的实例:
下面是对代码的分析:
1. `block_search` 函数接受三个参数:一个整数数组 `arr[]`,数组的长度 `n`,以及要查找的元素 `key`。
2. 首先计算块的大小 `block_size`,它是数组长度 `n` 的平方根的整数部分。
3. 初始化两个指针 `low` 和 `high`,分别指向数组的起始位置和结束位置。
4. 进入一个循环,当 `low` 小于等于 `high` 时继续执行。
5. 在每次循环中,计算中间索引 `mid`,并检查 `arr[mid]` 是否等于 `key`。如果相等,则返回 `mid`。
6. 如果 `arr[mid]` 小于 `key`,则将 `low` 更新为 `mid + 1`;否则,将 `high` 更新为 `mid - 1`。
7. 接下来检查当前块的边界条件。如果 `low` 和 `high` 都是块大小的倍数,那么检查这两个位置的元素是否等于 `key`。如果是,则返回相应的索引。
8. 如果循环结束后仍未找到 `key`,则返回 `-1` 表示未找到。
9. `main` 函数定义了一个示例数组 `arr[]`,并调用 `block_search` 函数来查找元素 `7`。根据查找结果输出相应的信息。
需要注意的是,这个实现假设输入数组已经排序且没有重复元素。如果数组中有重复元素或者未排序,该算法可能无法正确工作。此外,对于非完全平方数的数组长度,块大小可能会有所不同,这取决于如何定义块的大小。
#include <stdio.h>
#include <math.h>
int block_search(int arr[], int n, int key) {
int block_size = (int)sqrt(n);
int low = 0;
int high = n - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (arr[mid] == key) {
return mid;
} else if (arr[mid] < key) {
low = mid + 1;
} else {
high = mid - 1;
}
if (low % block_size == 0 && high % block_size == block_size - 1) {
if (arr[high] == key) {
return high;
}
if (arr[low] == key) {
return low;
}
}
}
return -1;
}
int main() {
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int n = sizeof(arr) / sizeof(arr[0]);
int key = 7;
int result = block_search(arr, n, key);
if (result != -1) {
printf("Key found at index: %d", result);
} else {
printf("Key not found");
}
return 0;
}
分块查找的优缺点:
4,线性表三种查找方法的比较:
折半折半查找的性能分析查找的性能分析
直至直至low>high时,查找
直至low>high时,查找失败
直至low>high时,查找失败
直至low>high时,查找失败
直至low>high时,查找失