查找——线性表之折半查找与分块查找

本篇文章主要介绍线性表中的折半查找与分块查找算法。

线性表的查找:

        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时,查找失 

  • 14
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值