查找表算法

本文介绍了查找表的概念及其基本操作,详细讲解了静态查找表的顺序查找、折半查找及分块查找算法,并提供了C语言实现示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

查找表是由同一类型的数据元素构成的集合。
一般对于查找表有一下几种操作:
1、在查找表中查找某个具体的数据元素;
2、在查找表中插入数据元素;
3、从查找表中删除数据元素。
静态查找表和动态查找表
在查找表中只做查找操作,不带动表中数据元素,称此类查找表为静态查找表;反之,在查找表中查找操作的同时进行插入数据或者删除数据的操作,称此类表为动态查找表。

静态查找表既可以使用顺序表表示,也可以使用链表结构表示。虽然一个是数组、一个链表,但两者在做查找操作时,基本上大同小异。

顺序查找的实现

静态查找表用顺序存储结构表示时,顺序查找的查找过程为:从表中的最后一个数据元素开始,逐个同记录的关键字做比较,如果匹配成功,则查找成功;反之,如果直到表中第一个关键字查找完也没有成功匹配,则查找失败。

#include <stdio.h>
#include <stdlib.h>
#define keyType int
typedef struct {
    keyType key;//查找表中每个数据元素的值
    //如果需要,还可以添加其他属性
}ElemType;
typedef struct{
    ElemType *elem;//存放查找表中数据元素的数组
    int length;//记录查找表中数据的总数量
}SSTable;
//创建查找表
void Create(SSTable **st,int length){
    (*st)=(SSTable*)malloc(sizeof(SSTable));
    (*st)->length=length;
    (*st)->elem =(ElemType*)malloc((length+1)*sizeof(ElemType));
    printf("输入表中的数据元素:\n");
    //根据查找表中数据元素的总长度,在存储时,从数组下标为 1 的空间开始存储数据
    for (int i=1; i<=length; i++) {
        scanf("%d",&((*st)->elem[i].key));
    }
}
//查找表查找的功能函数,其中key为关键字
int Search_seq(SSTable *st,keyType key){
    st->elem[0].key=key;//将关键字作为一个数据元素存放到查找表的第一个位置,起监视哨的作用
    int i=st->length;
    //从查找表的最后一个数据元素依次遍历,一直遍历到数组下标为0
    while (st->elem[i].key!=key) {
        i--;
    }
    //如果 i=0,说明查找失败;反之,返回的是含有关键字key的数据元素在查找表中的位置
    return i;
}
int main() {
    SSTable *st;
    Create(&st, 6);
    getchar();
    printf("请输入查找数据的关键字:\n");
    int key;
    scanf("%d",&key);
    int location=Search_seq(st, key);
    if (location==0) {
        printf("查找失败");
    }else{
        printf("数据在查找表中的位置为:%d",location);
    }
    return 0;
}

输入表中的数据元素:
1 2 3 4 5 6
请输入查找数据的关键字:
2
数据在查找表中的位置为:2

同时,在程序中初始化创建查找表时,由于是顺序存储,所以将所有的数据元素存储在数组中,但是把第一个位置留给了用户用于查找的关键字。例如,在顺序表{1,2,3,4,5,6}中查找数据元素值为 7 的元素,则添加后的顺序表为:


顺序表中的监视哨
图 1 顺序表中的监视哨


顺序表的一端添加用户用于搜索的关键字,称作“监视哨”。

放置好监视哨之后,顺序表遍历从没有监视哨的一端依次进行,如果查找表中有用户需要的数据,则程序输出该位置;反之,程序会运行至监视哨,此时匹配成功,程序停止运行,但是结果是查找失败。

折半查找算法

折半查找,也称二分查找,在某些情况下相比于顺序查找,使用折半查找算法的效率更高。但是该算法的使用的前提是静态查找表中的数据必须是有序的

例如,在{5,21,13,19,37,75,56,64,88 ,80,92}这个查找表使用折半查找算法查找数据之前,需要首先对该表中的数据按照所查的关键字进行排序:{5,13,19,21,37,56,64,75,80,88,92}

在折半查找之前对查找表按照所查的关键字进行排序的意思是:若查找表中存储的数据元素含有多个关键字时,使用哪种关键字做折半查找,就需要提前以该关键字对所有数据进行排序。

对静态查找表{5,13,19,21,37,56,64,75,80,88,92}采用折半查找算法查找关键字为 21 的过程为:
 


图 1 折半查找的过程(a)


如上图 1 所示,指针 low 和 high 分别指向查找表的第一个关键字和最后一个关键字,指针 mid 指向处于 low 和 high 指针中间位置的关键字。在查找的过程中每次都同 mid 指向的关键字进行比较,由于整个表中的数据是有序的,因此在比较之后就可以知道要查找的关键字的大致位置。

例如在查找关键字 21 时,首先同 56 作比较,由于21 < 56,而且这个查找表是按照升序进行排序的,所以可以判定如果静态查找表中有 21 这个关键字,就一定存在于 low 和 mid 指向的区域中间。

因此,再次遍历时需要更新 high 指针和 mid 指针的位置,令 high 指针移动到 mid 指针的左侧一个位置上,同时令 mid 重新指向 low 指针和 high 指针的中间位置。如图 2 所示:
 


图 2 折半查找的过程(b)
 

同样,用 21 同 mid 指针指向的 19 作比较,19 < 21,所以可以判定 21 如果存在,肯定处于 mid 和 high 指向的区域中。所以令 low 指向 mid 右侧一个位置上,同时更新 mid 的位置。
 


图 3 折半查找的过程(3)

当第三次做判断时,发现 mid 就是关键字 21 ,查找结束。

注意:在做查找的过程中,如果 low 指针和 high 指针的中间位置在计算时位于两个关键字中间,即求得 mid 的位置不是整数,需要统一做取整操作。

#include <stdio.h>
#include <stdlib.h>
#define keyType int
typedef struct {
    keyType key;//查找表中每个数据元素的值
    //如果需要,还可以添加其他属性
}ElemType;
typedef struct{
    ElemType *elem;//存放查找表中数据元素的数组
    int length;//记录查找表中数据的总数量
}SSTable;
//创建查找表
void Create(SSTable **st,int length){
    (*st)=(SSTable*)malloc(sizeof(SSTable));
    (*st)->length=length;
    (*st)->elem = (ElemType*)malloc((length+1)*sizeof(ElemType));
    printf("输入表中的数据元素:\n");
    //根据查找表中数据元素的总长度,在存储时,从数组下标为 1 的空间开始存储数据
    for (int i=1; i<=length; i++) {
        scanf("%d",&((*st)->elem[i].key));
    }
}
//折半查找算法
int Search_Bin(SSTable *ST,keyType key){
    int low=1;//初始状态 low 指针指向第一个关键字
    int high=ST->length;//high 指向最后一个关键字
    int mid;
    while (low<=high) {
        mid=(low+high)/2;//int 本身为整形,所以,mid 每次为取整的整数
        if (ST->elem[mid].key==key)//如果 mid 指向的同要查找的相等,返回 mid 所指向的位置
        {
            return mid;
        }else if(ST->elem[mid].key>key)//如果mid指向的关键字较大,则更新 high 指针的位置
        {
            high=mid-1;
        }
        //反之,则更新 low 指针的位置
        else{
            low=mid+1;
        }
    }
    return 0;
}
int main(int argc, const char * argv[]) {
    SSTable *st;
    Create(&st, 11);
    getchar();
    printf("请输入查找数据的关键字:\n");
    int key;
    scanf("%d",&key);
    int location=Search_Bin(st, key);
    //如果返回值为 0,则证明查找表中未查到 key 值,
    if (location==0) {
        printf("查找表中无该元素");
    }else{
        printf("数据在查找表中的位置为:%d",location);
    }
    return 0;
}

以图 1 的查找表为例,运行结果为:

输入表中的数据元素:
5 13 19 21 37 56 64 75 80 88 92
请输入查找数据的关键字:
21
数据在查找表中的位置为:4

总结

通过比较折半查找的平均查找长度,同前面介绍的顺序查找相对比,明显折半查找的效率要高。但是折半查找算法只适用于有序表,同时仅限于查找表用顺序存储结构表示。

当查找表使用链式存储结构表示时,折半查找算法无法有效地进行比较操作(排序和查找操作的实现都异常繁琐)。

 

分块查找算法,索引顺序查找算法

如,采用分块查找法在有序表 11、12、18、28、39、56、69、89、96、122、135、146、156、256、298 中查找关键字为 96 的元素。

査找特定关键字元素个数为 15,要求用户输入有序表各元素,程序输出査找成功与否,若成功,还显示元素在有序表中的位罝。

实现过程:

(1)定义结构体 index,用于存储块的结构,并定义该结构体数组 index_table。

(2)自定义函数 block_search(),实现分块查找。

(3) main() 函数作为程序的入口函数。程序代码如下:

#include <stdio.h>
struct index    //定义块的结构
{
    int key;    //块的关键字
    int start;    //块的起始值
    int end;    //块的结束值
}index_table[4];    //定义结构体数组
int block_search(int key,int a[])    //自定义实现分块查找
{
    int i,j;
    i=1;
    while(i<=3&&key>index_table[i].key)    //确定在哪个块中
        i++;
    if(i>3)    //大于分得的块数,则返回0
        return 0;
    j=index_table[i].start;    //j等于块范围的起始值
    while(j<=index_table[i].end&&a[j]!=key)    //在确定的块内进行顺序查找
        j++;
    if(j>index_table[i].end)    //如果大于块范围的结束值,则说明没有要査找的数,j置0
        j = 0;
    return j;
}
int main()
{
    int i,j=0,k,key,a[16];
    printf("请输入15个数:\n");
    for(i=1;i<16;i++)
        scanf("%d",&a[i]);    //输入由小到大的15个数
    for(i=1;i<=3;i++)
    {
        index_table[i].start=j+1;    //确定每个块范围的起始值
        j=j+1;
        index_table[i].end=j+4;    //确定每个块范围的结束值
        j=j + 4;
        index_table[i].key=a[j];    //确定每个块范围中元素的最大值
    }
    printf("请输入你想査找的元素:\n");
    scanf("%d",&key);    //输入要查询的数值
    k=block_search(key,a);    //调用函数进行杳找
    if(k!=0)
        printf("查找成功,其位置是:%d\n",k);    //如果找到该数,则输出其位置
    else
        printf("查找失败!");    //若未找到,则输出提示信息
    return 0;
}
运行结果:
请输入15个数:
11 12 18 28 39 56 69 89 96 122 135 146 156 256 298
请输入你想査找的元素:
96
查找成功,其位置是:9

技术要点:

分块査找也称为索引顺序査找,要求将待查的元素均匀地分成块,块间按大小排序,块内不排序,所以要建立一个块的最大(或最小)关键字表,称为索引表。

本实例中将给出的 15 个数按关键字大小分成了 3 块,这 15 个数的排列是一个有序序列,也可以给出无序序列,但必须满足分在第一块中的任意数都小于第二块中的所有数,第二块中的所有数都小于第三块中的所有数。当要査找关键字为 key 的元素时,先用顺序杳找在已建好的索引表中查出 key 所在的块中,再在对应的块中顺序查找 key,若 key 存在,则输出其相应位置,否则输出提示信息。

 

总结:二分查找和分块超查找要基于数据是有序的,才可以实现。所在在查找之前要进行数据的排序。

二叉排序树(二叉查找树)及C语言实现

http://c.biancheng.net/view/3431.html

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值