查找算法之哈希算法

本文详细介绍了哈希表的工作原理,包括其查找效率优势和解决哈希冲突的不同方法,如开放定址法、拉链法等。同时,重点讲解了布隆过滤器的概念,作为一种基于哈希冲突的过滤器,用于大数据中的重复元素检测。最后提供了C语言实现的哈希表代码示例。
摘要由CSDN通过智能技术生成

哈希表与布隆过滤器

一:哈希表:哈希表,是一种叫做哈希算法自然而然衍生出来的一种时间复杂度通常为o(!)的查找算法,哈希表的底层原理为数组下标以及对应值的映射,而数组通过下标index直接访问数组中所对应的值花费的时间复杂度是相对较少的,但是数组暴露的问题是不能动态分配所要的存储空间,导致有极大一部分资源会被浪费掉,哈希表同样也是通过下标来反映值,从高维到低微的映射,但是哈希表在查找元素时时间复杂度为o1,极大的减少时间复杂度的问题

二:哈希表和哈希算法:

哈希表存储的方式为取余存储

例如:val%size进行存储

比如我现在有一个值为6,此时哈希表的size长度为9,6取模于9为6所以存储在下标为6的地方

相同的原理我有一个值为7的要进行哈希表的村存储则会存储到哈希表下标为7的地方,总之就是val%size为多少就存储在哈希表下标为多少的地方

哈希函数
67

                            1         2          3            4          5            6          7           8          9

但是这样就会存在一个问题,我val=6和val=15时候所取余结果是相同的,这种情况就要将数值存储在相同的下标,但是又不满足数组唯一下标访问唯一值的原理,如下图

6/15

这种显然是不行的,所以就产生了新的名词叫做哈希冲突,我们把这种情况称作哈希冲突

既然有哈希冲突,那么肯定就有解决哈希冲突的办法,下面是解决哈希冲突的四种方法,我会将最常用并且最实用的方法换一种颜色来进行标记

1:哈希冲突解决办法一:

开放定址法:如果所存储的地方存在值

则从所在值的下标进行寻找找到一个没有存值的地方来将值存放进去

最初选用的地址存放的方法为遍历,但是由于遍历可利用性以及花费时间过多,后来改为平方相加存值,通过平方数的相加来大范围寻找是否有合适的空地址可以进行存放

2:哈希冲突的解决办法二:

再哈希法:没有合适的哈希地址存放,那就再造一个哈希地址进行存放

但是个人感觉这种方法不太实用,因为哈希函数的构造本身是有一定难度的,如果多次构造会产生代码结构错误或者突兀,总之就是这是方法偏向理论感觉会更多一点

3:哈希冲突的解决办法三:

建立公共溢出区:将所有没有合适地址存放的数值全部存入到公共溢出区种中去,最后再进行合理的分配,建立公共溢出区中的溢出区可以是多种数据结构比如说是栈,红黑二叉树,队列等多种数据结构

4:哈希冲突的解决办法四:

拉链法:拉链法是一种相比其他方法更加可以偏向实际操作和具体实现的一种方法,它的逻辑方法为将有冲突的地址存放到一起,将所有有冲突的数值通过指针指向的方法来在当前地址构造成为一条链表的方法,看图可能会比较清晰一点

   xxxxxxx

1
2
3
4
5
6
7
8

9

比如我在xxxxxx这个位置有1-9的数值的冲突,那么我就在这块地址构造一个链表用来存放相同的地址不同的冲突值,查找时候只需要锁定原哈希表地址下对应的链表结构来查找数值即可

三:哈希表衍生出的一种新的容器

布隆过滤器

它的底层原理是通过哈希函数的哈希冲突来进行搜索有无重复信息来进行过滤

比如一个巨大的随机数

比如python爬虫爬到的百亿条的网页地址都可以进行大概率的过滤

布隆过滤器是一种概率性过滤器,如果出现有重复元素可能是大概率出现过此元素,从而进行过滤,但是如果要求的数据必须准确和精确使用布隆过滤器可能会有点麻烦,但是如果布隆过滤器没有出现过则可以断定此时元素或者数据一定没有出现

如何增大布隆过滤的过滤概率,通过增加哈希函数的数量和增大布隆过滤器底层的标注空间的都可以进行增大布隆过滤器的效率

四:哈希表的代码演示

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

typedef struct Node
{
    char* s;//字符串数据类型,想要是其他数据结构可进行自行更换
    struct Node* next;
}Node;//哈希表中的链表的存储结构

typedef struct hash_table
{
    Node* vector;
    int count, size;//conunt代表一个hashtable中存放的元素数量,size代表哈希表中的数组长度
}hash_table;

Node* getnewnode(char *data)
{
        Node*p=(Node*)malloc(sizeof(Node));
        p->next = NULL;
        p->s = data;
        return p;
}//Node节点初始化

hash_table* getnewhashtable(int n)
{
    hash_table* p = (hash_table*)malloc(sizeof(hash_table) * n);
    p->vector = (Node*)malloc(sizeof(Node) * n);
    p->count = 0;
    p->size = n;
    return p;
}//hash表中的表类初始化和节点初始化

int hash_func(char* s)
{
    //字符串哈希算法
    int need = 131, hash = 0;
    for (int i = 0; i < s[i]; i++){
        hash *= need + s[i];
    }
    return hash & 0xfffff;
}//哈希算法操作

bool findhash(hash_table* h, char* s)
{
    if (h == NULL) return false;
    int hcode = hash_func(s);
    int index = hcode % h->size;
    Node* p = h->vector[index].next;
    while (p)
    {
        if (p->s == h->vector[index].s)
        {
            return true;
        }
        p = p->next;
    }
    return false;
}//查找操作

void swap1(int num1, int num2)
{
    hash_table* h1, * h2;
    int temp1 = h1->size;
    h1->size = h2->size;
    h2->size = temp1;
    return;
}//交换哈希表中的所改变的长度

void swap2(int num1, int num2)
{
    hash_table* h1, * h2;
    int temp1 = h1->count;
    h1->count = h2->count;
    h2->count = temp1;
    return;
}//交换哈希表中的元素

void swap3(Node* p1, Node* p2)
{
    hash_table* h1, * h2;
    //交换节点next
    Node* temp = h1->vector->next;
    h1->vector->next = h2->vector->next;
    h2->vector->next = temp;
    //交换节点的字符串数值
    char *temps = h1->vector->s;
    h1->vector->s = h2->vector->s;
    h2->vector->s=temps;
    return;
}

void swaphash(hash_table* h1,hash_table *h2)
{
    swap1(h1->count, h2->count);
    swap2(h1->size, h2->size);
    swap3(h1->vector, h2->vector);
}//哈希表中所有内置数据的操作

void expand(hash_table* h)
{
    //哈希表扩容操作
    hash_table* new_hash_table = getnewhashtable(h->size * 2);
    for (int i = 0; i < h->size; i++)
    {
        Node* p = h->vector[i].next;
        while (p)
        {
            insert(new_hash_table, p->s);
            p = p->next;
        }
    }
    swaphash(h, new_hash_table);
    return;
}

bool insert(hash_table* h, char* s)
{
    if (h->count <= h->size) expand(h);
    if (h == NULL) return false;
    int hcode = hash_func(s);
    int index = hcode % h->size;    
    //冲突处理
    //拉链法
    Node* p = getnewnode(s);//将链表封装为哈希表的节点
    p->next = h->vector[index].next;
    h->vector[index].next = p;
    h->count += 1;
    return true;    
}


void deletehashtable(hash_table* h)
{
    //先回收hash表中的节点
    //再回收哈希表
    if (h == NULL)return;
    for (int i = 0; i < h->size; i++)
    {
        //销毁数据存储区中的每一个链表结构
        Node* p = h->vector[i].next, * q;
        while (p)
        {
            q = p->next;
            deleteNode(p);
            p = q;
        }
    }
    free(h->vector);//回收hash表中的数据空间
    free(h);//回收hash表中的本身的空间
    return;
}

void deleteNode(Node *p)
{
    if (p == NULL) return;
    if (p->s) free(p->s);
    free(p);
    return;
}//回收hash表中链表的节点

int main()
{
    return 0;
}

上面代码为hash表的操作实现,具体有注释进行各个操作

如何应用哈希算法来进行解题和设计见下一文章,hash的具体实现和用法

如有侵权,请联系我

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值