首页 > 其他分享 >[数据结构] 哈希表 (开放寻址法+拉链法)

[数据结构] 哈希表 (开放寻址法+拉链法)

时间:2023-02-05 12:44:24浏览次数:52  
标签:include return int next 寻址 哈希 Hash 数据结构

哈希表

哈希表的基本概念

哈希表 Hash table 是一种提供快速查找和插入元素的数据结构,也称散列表。哈希表是基于数组的扩展,一般利用数组的下标作为哈希表的键值。

哈希表存储的是由键(key)和值(value)组成的数据。键值 key 是由哈希函数得到的。

哈希函数

除留余数法

除留余数法是一种广泛运用的哈希函数,其用插入元素 mod 哈希表长度来得到关键字。由于哈希表是基于数组的,假如要插入负数元素,在C、C++中取模后还是负数,但是数组的下标不可以为负,所以哈希函数写成下列形式:

其中 x 为插入元素, N 为哈希表长度。这样取模后,得到的是非负数。

除留余数法函数

int Hash(int x){
    return ((x % N) + N) % N;
}


处理哈希冲突

哈希表中每个键值很可能对应了多个元素,所以哈希冲突不可避免,常用解决哈希冲突的方法有两种,分别为 开放地址法拉链法

开放寻址法

开放寻址法即当元素不能直接存放在哈希函数计算出来的数组下标时,就需要寻找其他位置来存放。
我们一般采用线性探测,当对应键位置 k 已经存在元素时,则将 k 进行加一,探测下一个位置是否为空。但这样的处理冲突方法比较低效。

开放寻址法代码

#include<iostream>
#include<cstring>
using namespace std;

static const int N = 14, inf = 0x3f3f3f3f;

int h[N];

void init(){
    memset(h, 0x3f, sizeof(h));
}

int Hash(int x){
    return ((x % N) + N) % N;
}

bool find(int x){
    int k = Hash(x);
    if(h[k] == inf) return false; //当前键值对应为空
    while(h[k] != x){
        k = (k + 1) % N;
        if(h[k] == inf || k == Hash(x))
            return false;         //回到起点
    }
    return true;
}

void insert(int x){
    int k = Hash(x);
    while(h[k] != inf) k = (k + 1) % N;
    h[k] = x;
}

int main(){
    init();

    for(int i = 9; i <= 15; i++)
        insert(i);

    for(int i = 7; i <= 13; i++)
        if(find(i))
            cout<<"YES"<<endl;
        else
            cout<<"NO"<<endl;
    cout<<endl;
}

拉链法

拉链法是比较常用的处理哈希冲突的方法。我们使用一个数组,数组的每个位置存放一个链表,这样当对应键存在多个元素需要存放时,可以很方便地将元素插入到对应链表中。

拉链法使用的结构和图的领接表很像,后续也称之为邻接表吧。

拉链法 邻接表

#include<iostream>
#include<cstdlib>
using namespace std;

static const int N = 7;

typedef struct node{
	
    int val;
    node *next;

}node;

node* h[N];       //邻接表

void init(){
    for(int i = 0; i < N; i++){
        h[i] = new node;
        h[i]->val = -1;
        h[i]->next = NULL;
    }
}

int Hash(int x){
    return ((x % N) + N) % N;
}

bool find(int x){
    int k = Hash(x);
    for(node* p = h[k]->next; p; p = p->next)
        if(p->val == x)
            return true;
    return false;
}

void insert(int x){
    int k = Hash(x);
    node* cur = new node;
    cur->val = x;
    cur->next = h[k]->next;    //头插法
    h[k]->next = cur;
}

int main(){
    init();

    for(int i = 1; i <= 17; i++)
        insert(i);

    for(int i = 15; i <= 20; i++)
        if(find(i))
            cout<<"YES"<<endl;
        else
            cout<<"NO"<<endl;
    cout<<endl;

    for(int key = 0; key <= 6; key++){
        node* t = h[key]->next;
        cout<<"key = "<<key<<": ";
        while(t){
            cout<<t->val<<' ';
            t = t->next;
        }
        cout<<endl;
    }
}

拉链法 vector邻接表

相关试题 Acwing 840.模拟散列表
也可以用 vector 来实现邻接表。

#include<iostream>
#include<vector>
using namespace std;

const int N = 100003;
vector<int> h[N];

void insert(int x){
    int k = (x % N + N) % N;  //余数可能为负数
    h[k].push_back(x);
}

bool find(int x){
    int k = (x % N + N) % N;
    for(auto &cur : h[k])
        if(cur == x)
            return true;
    return false;
}

int main(){
    cin.tie(0); cout.tie(0);
    int n;  cin>>n;

    while(n--){
        char op;
        int x;
        cin>>op>>x;
        
        if(op == 'I')
            insert(x);
        else{
            if(find(x))
                puts("Yes");
            else
                puts("No");
        }
    }
}

拉链法 链式前向星

用链式前向星相比 vector 容器更快,这种链式结构在竞赛中用得比较多。

#include<iostream>
#include<cstring>
using namespace std;

const int N = 100003;
int Hash[N], e[N], ne[N], idx;

void insert(int x){
    int k = (x % N + N) % N;  //余数可能为负数
    e[idx] = x;
    ne[idx] = Hash[k];
    Hash[k] = idx++;
}

bool find(int x){
    int k = (x % N + N) % N;
    for(int i = Hash[k]; i != -1; i = ne[i])
        if(e[i] == x)
            return true;
    return false;
}

int main(){
    cin.tie(0); cout.tie(0);
    int n;  cin>>n;
    
    memset(Hash, -1, sizeof(Hash));

    while(n--){
        char op;
        int x;
        cin>>op>>x;
        
        if(op == 'I')
            insert(x);
        else{
            if(find(x))
                puts("Yes");
            else
                puts("No");
        }
    }
}

标签:include,return,int,next,寻址,哈希,Hash,数据结构
From: https://www.cnblogs.com/MAKISE004/p/17093103.html

相关文章

  • 数据结构
    优化代码结构、增大运行效率记录某种事件或某种信息的载体,如何管理数据编程之美线性表:链表、栈、队列顺序:数组定长,取数快链式:指针,前驱和后继,不定长线性存储:学生信息......
  • 哈希表
    哈希表(hashtable)又叫散列表,是一种关联式容器,存储的是一对值,一般是一个key对应一个value(又叫键值对)。数组、链表、栈、队列都是序列式容器,存储的都是一个元素。C++ST......
  • [数据结构] 树、森林的遍历
    树的遍历树的遍历方式有先根遍历和后根遍历。在下面树的遍历中,采用的都是孩子兄弟表示法构建的树。树的先根遍历树的先根遍历步骤先根遍历就是先访问树的根节点,然后再......
  • 数据结构-实现逆波兰计算器
     packagecom.stack;importjava.util.ArrayList;importjava.util.List;importjava.util.Stack;publicclassPoland{publicstaticvoidmain(String[]a......
  • Fabric2.x中Raft共识算法核心数据结构
    一、共识算法可插拔的代码体现Chain接口HyperledgerFabric的共识算法是可插拔的,在代码上体现为Chain接口,所有不同的共识算法均可根据Chain接口进行具体实现,目前fabric支......
  • [数据结构] 树、二叉树、森林的转换
    树树的表示方法双亲表示法用一组地址连续的存储单元来存放树中的各个节点,每一个节点中有一个数据域和一个指针域,数据域用来存储树中该节点本身的值;另一个指针域用来存储......
  • 【算法】二分法 ① ( 二分法基本原理简介 | 二分法与哈希表对比 | 常见算法对应的时间
    文章目录​​一、二分法基本原理简介​​​​1、二分法与哈希表对比​​​​2、二分法具体步骤​​​​二、常见算法对应的时间复杂度​​一、二分法基本原理简介二分法算......
  • 【C语言 数据结构】数组与对称矩阵的压缩存储
    文章目录​​数组的定义​​​​数组的顺序表示和实现​​​​顺序表中查找和修改数组元素​​​​矩阵的压缩存储​​​​特殊矩阵​​​​稀疏矩阵​​数组的定义提到数组......
  • 秋招备战——数据结构
    二叉树满二叉树,深度为i,总共有pow(2,i)-1个节点的二叉树称为满二叉树哈夫曼树:带权路径最短的二叉树称为哈夫曼树或最优二叉树。终端结点数为n0,度为2的结点数为n2,那么n0=......
  • 数据结构-小孩出圈问题(约瑟夫环问题)
    假设有m个小孩,数到n的小孩出列,直到全部出去为止。使用环形链表解决约瑟夫环问题。packagecom.linkedlist;publicclassJosephuLinkeslist{publicstaticvoid......