数据结构——查找

本文详细介绍了查找算法,包括顺序查找、二分查找(折半查找)、分块查找和Hash表查找,以及它们在不同场景下的应用。重点讨论了如何处理哈希表中的冲突问题,如开放地址法和链地址法,并给出了实际的哈希表实现代码示例。

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


查找概念

设记录表L=(R1 R2……Rn),其中Ri(l≤i≤n)为记录,对给定的某个值k,在表L中确定key=k的记录的过程,称为查找
若表L中存在一个记录Ri的key=k,记为Ri.key=k,则查找成功,返回该记录在表L中的序号i(或Ri 的地址),否则(查找失败)返回0(或空地址Null)

提示:以下是本篇文章正文内容,下面案例可供参考

一、查找方法

查找方法有顺序查找、折半查找、分块查找、Hash表查找等等。查找算法的优劣将影响到计算机的使用效率,应根据应用场合选择相应的查找算法

1.顺序查找

.顺序查找
应用:顺序查找适合于存储结构为顺序存储或链接存储的线性表,最基本的查找技术。
基本思想:顺序查找也称为线形查找,属于无序查找算法。从数据结构线形表的一端开始,顺序扫描,依次将扫描到的结点关键字与给定值k相比较,若相等则表示查找成功;若扫描结束仍没有找到关键字等于k的结点,表示查找失败。
顺序查找的时间复杂度为:O(n)。

2.二分查找(折半查找)

应用:折半查找的前提条件是需要有序表顺序存储,对于静态查找表,一次排序后不再变化,折半查找能得到不错的效率。但对于需要频繁执行插入或删除操作的数据集来说,维护有序的排序会带来不小的工作量,那就不建议使用
说明:元素必须是有序的,如果是无序的则要先进行排序操作
基本思想:也称为是折半查找,属于有序查找算法。用给定值k先与中间结点的关键字比较,中间结点把线形表分成两个子表,若相等则查找成功;若不相等,再根据k与该中间结点关键字的比较结果确定下一步查找哪个子表,这样递归进行,直到查找到或查找结束发现表中没有这样的结点
简单理解其实就像电视中的看物品猜价格,先取个值,然后说大了小了,然后逐渐缩小范围,得到最后的物品价格
复杂度分析:最坏情况下,关键词比较次数为log2(n+1),且期望时间复杂度为O(log2n);
在这里插入图片描述

3.分块查找

3.分块查找

说明:分块查找又称索引顺序查找,它是顺序查找的一种改进方法。在块内还能再使用折半查找,方法是灵活的。
算法思想:将n个数据元素"按块有序"划分为m块(m ≤ n)。每一块中的结点不必有序,但块与块之间必须"按块有序";即第1块中任一元素的关键字都必须小于第2块中任一元素的关键字;而第2块中任一元素又都必须小于第3块中的任一元素,……
算法流程:
step1 先选取各块中的最大关键字构成一个索引表;
step2 查找分两个部分:先对索引表进行二分查找或顺序查找,以确定待查记录在哪一块中;然后,在已确定的块中用顺序法进行查找
在这里插入图片描述

4.Hash

在这里插入图片描述

当要查找key=k的记录时,通过关系f就可得到相应记录的地址而获取记录,从而免去了key的比较过程
这个关系f就是所谓的Hash函数(或称散列函数、杂凑函数),记为H(key)
它实际上是一个地址映象函数,其自变量为记录的key,函数值为记录的存储地址(或称Hash地址)

不同的key可能得到同一个Hash地址,即当keyl≠key2时,可能有H(key1)=H(key2),此时称key1和key2为同义词。这种现象称为“冲突”或“碰撞”,因为一个数据单位只可存放一条记录。
一般,选取Hash函数只能做到使冲突尽可能少,却不能完全避免。这就要求在出现冲突之后,寻求适当的方法来解决冲突记录的存放问题

处理冲突的方法:
处理冲突的方法一般为:在地址j的前面或后面找一个空闲单元存放冲突的记录,或将相冲突的诸记录拉成链表
在处理冲突的过程中,可能发生一连串的冲突现象,即可能得到一个地址序列H1、H2……Hn,Hi∈[0,m-l]。H1是冲突时选取的下一地址,而H1中可能己有记录,又设法得到下一地址H2……直到某个Hn不发生冲突为止。这种现象称为“聚积”,它严重影响了Hash表的查找效率
冲突现象的发生有时并不完全是由于Hash函数的随机性不好引起的,聚积的发生也会加重冲突
还有一个因素是表的装填因子α,α=n/m,其中m为表长,n为表中记录个数。一般α在0.7~0.8之间,使表保持一定的空闲余量,以减少冲突和聚积现象
这里主要用了两种方法一个是开放地址法,一个是链地址的方法。

开放地址法:
Hi=(H(key)+di)%m
原来hash函数基础上,在它的附近找空间
在这里插入图片描述
在这里插入图片描述
但是出现一直冲突就聚集了,这种方法就不能用了!!!
但是出现一直冲突就聚集了,这种方法就不能用了!!!

链地址法解决冲突的优点:无聚积现象;删除表中记录容易实现
在这里插入图片描述

Hash插入查找

#define N 15
typedef int datatype;
 
typedef struct node {
    datatype key;
    datatype value;
    struct node * next;
}listnode, *linklist;
 
typedef struct {
    listnode data[N];
}hash;
 
hash * hash_create();
int hash_insert(hash *HT, datatype key);
linklist  hash_search(hash *HT, datatype key);
hash * hash_create() {
    hash * HT;
 
    if ((HT = (hash *)malloc(sizeof(hash))) == NULL) {
        printf("malloc failed\n");
        return NULL;
    }
 
    memset(HT, 0, sizeof(hash));
 
    return HT;
}
 
int hash_insert(hash *HT, datatype key) {
    linklist p, q;
 
    if (HT == NULL) {
        printf("HT is NULL\n");
        return -1;
    }
 
    if ((p = (linklist)malloc(sizeof(listnode))) == NULL) {
        printf("malloc failed\n");
        return -1;
    }
    p->key = key;
    p->value = key % N;
    p->next = NULL;
 
    q = &(HT->data[key % N]);
 
    while (q->next && q->next->key < p->key ) {
        q = q->next;
    }
 
    p->next = q->next;
    q->next = p;
 
    return 0;
 
}
 
linklist  hash_search(hash *HT, datatype key) {
    linklist p;
 
    if (HT == NULL) {
        printf("HT is NULL\n");
        return NULL;
    }
 
    p = &(HT->data[key % N]);
 
    while (p->next && p->next->key != key) {
        p = p->next;
    }
 
    if (p->next == NULL) {
        return NULL;
    } else {
        printf("found\n");
        return p->next;
    }
}
int main(int argc, const char *argv[])
{
    hash * HT;
    int data[] = {23, 34, 14, 38, 46, 16, 68, 15, 7, 31, 26};
    int i;
    int key;
    linklist r;
 
    if ((HT = hash_create()) == NULL) {
        return -1;
    }
 
    for (i = 0; i < sizeof(data)/sizeof(int); i++) {
        hash_insert(HT, data[i]);
    }
 
    printf("input:");
    scanf("%d", &key);
    r = hash_search(HT, key);
    if (r == NULL) 
        printf("not found\n");
    else
        printf("found:%d %d\n", key, r->key);
 
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值