查找算法之散列表

这篇具有很好参考价值的文章主要介绍了查找算法之散列表。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一.说明

刚好复习数据结构,前面几篇博客我们知道了顺序查找、二分查找、分块查找、树形查找(二叉排序树、平衡二叉树、红黑树、B树和B+树),这一篇博客介绍常用查找算法中的最后一个算法——散列表(哈希查找)。

同时,刚好参加的新星计划:数据结构与算法通道在周末结束,这一篇博客也是本周的学习任务。

二.散列表的基本概念

1.名词解释

散列表(Hash table)是一种基于哈希表实现的数据结构,它通过将给定的键值映射到一个特定的索引位置来存储和查找数据元素。散列表通常使用数组作为底层数据结构,并且需要一个可靠的散列函数来计算每个键对应的索引位置。

散列函数(Hash function)是一种将任意长度的输入数据映射到固定长度输出的函数。散列函数通常用于计算散列表中元素的索引位置,它将每个键值转换为一个唯一的整数值,这个整数值可以作为该元素在散列表中的索引。散列函数的设计非常重要,因为它直接影响散列表的性能和效率。一个好的散列函数应当尽可能避免哈希碰撞,即不同的键值映射到相同的索引位置。

哈希冲突(Hash collision)指的是两个或多个键值被散列到了相同的索引位置。由于散列表的大小是有限的,因此哈希冲突是不可避免的。当发生哈希冲突时,我们需要采取一些方法来解决它,以确保散列表的正确性和效率。常见的解决哈希冲突的方法包括链式存储法和开放地址法等。

同义词(Synonyms)是指具有相同或相似含义的单词。在散列表中,不同的键值可能被映射到相同的索引位置,这些键值即为同义词。解决同义词问题的方法之一是使用拉链法进行链式存储,在相同的散列值下将多个数据元素连接成一个链表,以避免发生冲突。

2.百度补充

散列表(Hash table)是一种常见的数据结构,它具有快速插入、查找和删除元素的能力。散列表通过将每个元素映射到一个唯一的索引位置来实现这些操作,这个索引位置可以称为哈希值或散列值。

散列表包含一个固定大小的数组,通常被初始化为空。当要插入一个元素时,首先需要计算该元素的哈希值,并将其作为数组索引来存储该元素。如果两个元素具有相同的哈希值,则会发生哈希冲突,解决哈希冲突的方法包括使用链式存储或开放地址法等。

在查询一个元素时,我们首先计算该元素的哈希值,并在数组中检查该位置是否存在元素。如果该位置为空,则表示该元素不存在于散列表中;否则,需要进行进一步的比较来确定该元素是否是所需的元素。

散列表是一种高效的数据结构,在大多数情况下,它的插入、查找和删除操作的时间复杂度都是 O(1)。但是,由于哈希冲突的存在,最坏情况下的时间复杂度可能会退化到 O(n),因此合理的哈希函数设计和处理哈希冲突的方法非常重要。

3.个人补充

散列表 == 哈希表

散列函数 == 哈希函数

散列查找 == 哈希查找

它们是在不同编程语言的不同称呼而已,其实本质上是一个概念。

三.散列函数的构造方法

1.设计注意事项

设计散列函数时需要注意以下几个点:

  1. 均匀性:散列函数应该将输入的数据均匀地散布到整个哈希表中,这样可以使得每个桶中的元素数量尽可能平衡,避免出现某些桶特别拥挤而导致性能急剧下降的情况。
  2. 碰撞率:碰撞是指两个不同的键映射到了同一个散列表位置的情况。散列函数的设计应该尽可能减少碰撞的发生。一般来说,可以采用开放寻址法或者链式哈希表来解决碰撞问题。但是过多的碰撞也会影响哈希表的性能,因此需要在散列函数设计中尽量避免碰撞。
  3. 易计算:散列函数的计算速度应该足够快,否则会影响哈希表的性能。不同类型的数据可能需要使用不同的散列函数实现。
  4. 抗攻击:散列函数应该能够有效地防止故意构造的输入数据,如恶意攻击者故意制造出大量的碰撞,使得哈希表的性能急剧下降。
  5. 随机性:散列函数应该尽可能地随机,这样可以降低攻击者进行散列冲突攻击的难度。随机性可以通过在散列函数中使用随机数或者加盐等方式实现。

2.除留余数法

这是一种最简单、最常用的方法,假设散列表表长为m,取一个不大于m但最接近m的质数p,利用以下公式把关键字转换成散列地址。

散列函数为 H(key) = key % p

除留余数法的关键是选好p,使得每个关键字通过该函数转换后等概率地映射到散列空间上的任意一个地址,从而尽可能减少冲突的可能性。

适用场景:较为常用,只要关键字是整数即可

3.直接定址法

直接取关键字的某个线性线性函数值为散列地址,散列函数为

H(key) = key 或 H(key) = a*key + b

式中,a和b是常数。这种方法最简单,且不会产生冲突。他适合关键字的分布基本连续的情况,若关键字分布不连续,空位较多、则会造成存储空间的浪费。

适用场景:关键字分布基本连续

4.数字分析法

数字分析法是散列函数构造方法之一,它的基本思想是利用待存记录的关键字中的数字特征来构造散列地址。

具体地说,数字分析法将关键字看做一个r进制数(r通常取10),然后从右往左取出若干个数位,将它们组成一个新的数,再对哈希表长m取模,作为该记录的散列地址。如果不足m位,则在左边补0。

这里需要注意的是,选择哪些数位作为新数的数位,以及新数的长度,都会直接影响到散列的性能。一般来说,应该选择数字分布比较均匀的数位,并且尽可能地选取多的数位才能保证较好的散列效果。

需要注意的是,数字分析法虽然简单易行,但是它对数据的要求比较高,而且也容易受到数据规律的影响,因此其散列性能可能并不理想。在实际应用中,还需要根据具体情况,结合其他散列函数构造方法,综合考虑选取最合适的散列函数。

5.平方取中法

平方取中法是散列函数构造方法之一,它的基本思想是将关键字的平方值取中间几位作为散列地址。

具体地说,假设关键字为n位数字,首先将其平方得到一个2n位的数,然后从中间取出m(通常取m=3或4)位数作为散列地址。如果不足m位,则在左边右边补0,如果超过m位,则截取中间的m位。

这种方法的优点是简单易行,且能够较好地利用关键字的各个位上的信息,提高散列性能。但是需要注意的是,在实际应用中,由于平方操作可能导致结果溢出,因此需要对溢出进行处理,以免影响散列效果。

总的来说,平方取中法是一种比较基础的散列函数构造方法,适用于一些简单的应用场景。在实际应用中,还需要根据具体情况选择其他散列函数构造方法,并综合考虑多个因素,如时间、空间复杂度等,才能构造出最优秀的散列函数。

四.处理冲突的方法

1.拉链法

拉链法(链接法、链地址法)(Chaining)是一种使用哈希表来解决冲突的方法,也称为链接法。在这种方法中,哈希表中每个位置都存储一个链表,如果多个键值映射到同一个位置,则它们将被添加到同一个链表中。

当需要查找一个键时,先计算它的哈希值,然后定位到对应的链表,并在链表中顺序查找。由于哈希表的大小有限,而数据量可能很大,所以在设计时需要考虑好哈希函数的设计,以减少冲突的发生,从而提高查询效率。

当添加一个新的键值对时,也需要先计算其哈希值,然后定位到对应的链表,将其添加到链表的末尾即可。

拉链法虽然处理冲突的效果较好,但它会占用更多的内存空间,因为每个位置都需要维护一个链表。此外,在遍历整个链表时,查询效率可能会受到影响,特别是当链表过长时。

2.开放地址法

开放地址法是一种常见的解决哈希冲突问题的方法。

当使用哈希表时,可能会出现多个键被映射到同一个哈希槽上,这就是哈希冲突。开放地址法的主要思想是在发生冲突时,继续探测下一个哈希槽,直到找到空槽或者已经探测过所有的槽为止。

开放地址法有几种不同的探测方法,包括线性探测、二次探测、双重哈希等。其中,线性探测是最简单、最常用的一种方法,其基本思路是:如果当前哈希槽已经被占用,则依次查看下一个槽,直到找到一个空槽。

例如,假设使用哈希表来存储字符串类型的键,哈希函数为将字符串转换为整数后取余数,当出现冲突时,采用线性探测。当插入键值对时,如果计算出的哈希值已经被占用,则从该位置往后一个一个查找,直到找到一个空槽为止。如果整个哈希表都被查找过了,但仍然没有找到空槽,那么就需要扩容,重新分配内存空间。

虽然开放地址法是一种简单有效的解决哈希冲突的方法,但是它仍然存在一些问题,例如当哈希表中元素过多时,探测时间会变长,甚至可能导致性能下降。因此,在实际应用中,需要根据具体情况选择合适的哈希函数和解决冲突的方法。

五.散列查找及性能分析

散列查找(Hash Lookup)是一种基于哈希表的查找算法。它通过将关键字映射到哈希表的一个位置上,从而加快查找速度。在散列查找中,哈希函数起着关键作用。哈希函数可以将待查找的关键字映射到哈希表中的一个位置上,并且保证不同的关键字映射到不同的位置上。

散列查找的时间复杂度通常为O(1),因为只需要计算一次哈希值即可在常数时间内访问到对应的数据。但是,在实际应用中,由于哈希冲突的出现,可能会导致查找效率下降。因此,如何解决哈希冲突也是散列查找性能优化的关键。

一些常见的解决哈希冲突的方法包括:链式法、开放地址法等。在使用链式法时,每个哈希槽都保存一个指向链表头结点的指针。如果哈希冲突发生,就将新元素插入到对应槽的链表尾部。在使用开放地址法时,当哈希冲突发生时,可以尝试探测下一个空槽,直到找到合适的位置为止。

在实际应用中,选择合适的哈希函数和解决哈希冲突的方法非常重要。一个好的哈希函数应当具有较低的哈希冲突率,并且能够均匀地将关键字映射到哈希表上。而解决哈希冲突的方法则需要根据具体情况来选取,不同的方法对应着不同的性能表现。

总之,散列查找是一种高效的查找算法,在大规模数据处理中得到了广泛的应用。在实际应用中,通过优化哈希函数和解决哈希冲突的方法,可以进一步提高散列查找的性能。

六.C语言实现散列查找

下面是一个简单的C语言实现散列查找的例子,使用链式法解决哈希冲突:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define TABLE_SIZE 10

// 定义哈希表中的数据结构
typedef struct node {
    char key[20]; // 键值
    int value; // 值
    struct node *next; // 指向下一个节点的指针
} Node;

// 定义哈希表结构体
typedef struct hashtable {
    Node *table[TABLE_SIZE]; // 存储元素的数组
} Hashtable;

// 初始化哈希表
void initHashtable(Hashtable *ht) {
    int i;
    for (i = 0; i < TABLE_SIZE; i++) {
        ht->table[i] = NULL;
    }
}

// 计算哈希值
int hash(char *key) {
    int sum, i;
    for (sum = 0, i = 0; key[i] != '\0'; i++) {
        sum += key[i];
    }
    return sum % TABLE_SIZE;
}

// 向哈希表中插入元素
void insertElement(Hashtable *ht, char *key, int value) {
    int h = hash(key);
    // 创建新节点
    Node *newNode = (Node *) malloc(sizeof(Node));
    strcpy(newNode->key, key);
    newNode->value = value;
    newNode->next = NULL;

    if (ht->table[h] == NULL) { // 如果该位置没有元素,则直接插入
        ht->table[h] = newNode;
    } else { // 如果该位置已经有元素,则使用链式法解决冲突
        Node *p = ht->table[h];
        while (p->next != NULL) {
            p = p->next;
        }
        p->next = newNode;
    }
}

// 从哈希表中查找元素
int findElement(Hashtable *ht, char *key) {
    int h = hash(key);
    Node *p = ht->table[h];
    while (p != NULL) {
        if (strcmp(p->key, key) == 0) {
            return p->value;
        }
        p = p->next;
    }
    return -1; // 没有找到
}

// 测试函数
int main() {
    Hashtable ht;
    initHashtable(&ht);

    insertElement(&ht, "apple", 10);
    insertElement(&ht, "banana", 20);
    insertElement(&ht, "orange", 30);

    printf("The value of apple is %d\n", findElement(&ht, "apple"));
    printf("The value of banana is %d\n", findElement(&ht, "banana"));
    printf("The value of orange is %d\n", findElement(&ht, "orange"));

    return 0;
}

在上面的例子中,我们定义了一个哈希表结构体Hashtable,其中包含一个数组table,用于存储元素。每个元素都是一个指向链表头节点的指针,如果哈希冲突发生,则将新元素插入到对应槽的链表尾部。

在实现中,我们先通过hash函数计算出待查找元素的哈希值,然后根据哈希值找到元素所在槽的指针。如果该指针为NULL,则说明该位置还没有元素,直接插入即可;否则,我们需要遍历链表,将新元素插入到链表尾部。

在查找时,我们也是先计算出哈希值,然后遍历对应槽的链表,在其中查找待查找的元素。如果找到了,则返回对应的值;否则,返回-1表示没有找到。

以上就是一个简单的C语言实现散列查找的例子,你可以参考此例子来实现自己的散列查找程序。

说明:

新星计划:数据结构与算法,@西安第一深情,创作打卡4!文章来源地址https://www.toymoban.com/news/detail-465681.html

到了这里,关于查找算法之散列表的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 使用Python在列表中查找值

    使用 Python 中的 in 操作符或者 index() 方法在列表中查找特定值, 要在一个列表中查找特定的值,可以使用 Python 中的 in 操作符或者 index() 方法。 下面是使用 in 操作符的示例代码: my_list = [1, 2, 3, 4, 5] if 3 in my_list:     print(\\\"3 存在于列表中\\\") else:     print(\\\"3 不存在于列表中

    2024年02月04日
    浏览(26)
  • 【数据结构】——查找、散列表的相关习题

    1、顺序查找适用于存储结构为()的线性表。 A、顺序存储结构或者链式存储结构 B、散列存储结构 C、索引存储结构 D、压缩存储结构 解析: (A) 顺序查找 属于线性查找,从线性表的一端开始,依次检查所给定的是否满足条件,若找到符合条件的元素,则查找成功

    2024年02月04日
    浏览(48)
  • 如何在Python 查找两个列表之间的差异?

    在处理数据和进行列表操作时,经常需要查找两个列表之间的差异。Python 提供了多种方法来实现这个目标,从简单的循环比较到使用内置函数和库函数等。本文将详细介绍几种常用的方法,帮助您在 Python 中查找两个列表之间的差异。 最基本的方法是使用循环逐个比较列表中

    2024年02月11日
    浏览(44)
  • 哈希表(散列表)的平均查找成功/失败长度

    计算哈希地址的方法,称之为哈希函数。 常见的计算哈希地址方法有: 1、直接定址法 2、除留余数法 3、数字分析法 4、平方取中法 本文所分析的是使用除留余数法计算哈希地址这类,的平均查找成功长度和查找失败长度 对于除留余数法的哈希函数(散列函数) H(key) = key

    2024年02月07日
    浏览(47)
  • python查找列表元素位置、个数、索引的方法

    引言:本文整理了python 查找列表元素位置、个数、索引 的方法(大全),主要内容包括 一、index()方法查找列表元素、 二、count()统计列表元素个数、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧。         在列表操作中查找列

    2024年02月11日
    浏览(47)
  • 如何在Python中查找列表中是否存在某个元素

    如何在Python中查找列表中是否存在某个元素 在Python中,我们经常需要在列表中查找特定的元素。幸运的是,Python提供了多种方法来实现这一目标。下面将介绍几种常见的方法,以帮助您在列表中查找元素。 方法一:使用\\\"in\\\" Python中的列表数据结构已经内置了用于检查元

    2024年02月06日
    浏览(59)
  • Python+Selenium实现列表元素的查找定位及删除操作

    获取列表(单页)全部数据 删除某行元素 查找并删除元素(目前仅支持删除单条数据,循环删除待继续研究) 结果示例

    2024年02月13日
    浏览(64)
  • 高级网络应用复习——TCP与UDP,ACL列表, 防火墙,NAT复习与实验(带命令)

            作者简介:一名在校云计算网络运维学生、每天分享网络运维的学习经验、和学习笔记。   座右铭:低头赶路,敬事如仪 个人主页:网络豆的主页​​​​​​ 目录  前言 一.知识点总结 1.传输层的

    2024年02月01日
    浏览(43)
  • git提交文件的时候与别人刚好修改的是同一个文件,进而导致冲突

    日常大家一个项目组中所有人共同维护一个项目,时常会有多人修改了同一个文件的情况,提交的时候又刚好在同一个时间段提交,先提交的修改发起merge request被合并,后提交的人发起merge request后就会产生冲突,这样就有人告诉你,你的代码有冲突。这时就需要你来解决冲

    2024年02月15日
    浏览(51)
  • chatgpt赋能python:Python中如何查找列表中元素的下标

    在Python编程中,经常需要查找列表中某个元素的下标。这种操作在数据分析、机器学习等领域中非常常见。本文将介绍Python中如何查找列表中元素的下标。 Python中的列表有一个常用的方法 index() ,用于查找给定元素在列表中的第一个出现位置的下标。示例代码如下: 如上所

    2024年02月14日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包