1.哈希表

2.哈希函数

3.哈希冲突

哈希表

哈希表是一种按key-value存储的数据结构,也称散列表。

之前的数组、树和图等等查找一个值时都要与结构中的值相比较,查找的效率取决于比较的次数。

而哈希表因为key与value对应,则可以在较少的比较次数中找到元素。

哈希函数

哈希表是对于给定的一个key,建立一个函数H(key),y =H(key)即为元素key在数组内的储存位置。

1.直接定址法

直接建立一个与key相关的线性函数形如:H(key)=k*key+b

2.数字分析法

就是找找规律,尽量让大多数H(key)落在不同区间,例如对于身份证号码,前面的数字重复率太高,可以考虑用出生年月日计算当key。

3.平方取中法

取关键字平方后的中间几位当做哈希地址。

4.折叠法

将关键字分成位数相同的几个部分(最后一部分位数可以不同),然后取这几部分的叠加和作为哈希地址。

关键字位数很多,并且数字分步均匀时可以考虑采用折叠法。

 5.除留余数法

形如H(key) = key %m.

6.随机数法(还莫得理解)

哈希冲突

当key过多时,就容易发生多个key对于H出现同样的结果,就是它们会占用数组同一个位置,这就是哈希冲突。

冲突是不可避免的,我们要做的就是要尽可能的使冲突达到最小。

常用的处理冲突的方法有:

1.开放定址法

   这个时候H* = (H(key)+di)%m   ,  di = 1、2、3........m-1

 这里对于di 的值又有三种取法:

  1)线性探测再散列:

     顾名思义,di取值为1,2,3.....m-1,也就是当前位置已经有元素时,会像下一个位置走。

  2)二次探测再散列

    这里di 取值为 1^2 、 -1^2 、2^2、-2^2.......- (m/2)^2  , m为散列表长度,若是算出key+di为负数,则加上m再取余,也就是移到了后面,即-1就移到最后一位。 

  3)伪随机探测再散列(这个还没搞懂)

 

2.链地址法

链地址法,看图理解。

经过哈希函数后,对于处于同一位置的元素,用链表将它们储存。

  

(图源自网络)

3.再哈希法

  Hi = RHi(key), i = 1,2,3.....k

 RHi 是不同的函数,在用一个哈希函数产生冲突时就调用另一个,直到不产生冲突为止。

 4.建立公共溢出表

也就是建立另一个区间,产生冲突的都放进另一个区间。感觉不太好用,冲突多了比较次数又多了。

下面用除余法和链地址法(尾接法)写的代码如下:

#include<iostream>
using namespace std; #define hashsize 11 struct Node{//链表节点
int data;
Node *next;
int flag;
Node(){
flag=;
next=NULL;
}
Node (int num){
flag=;
data=num;
next=NULL;
}
}; class Hash{
private:
Node *hashtable;
int len;//记录现在数据个数 public:
Hash(){
len=;
hashtable = new Node[hashsize];
}
~Hash(){
}
//获得key值 ,哈希函数
int getKey(int num){
return num%hashsize;
}
//往表中插入元素 ,链接法
void insertData(int num){
int key = getKey(num);//获得key值 if(hashtable[key].flag==){//此时当前key位置无数据
hashtable[key].data=num;
hashtable[key].flag=;
}
else{//尾插入法
Node *tmp = &hashtable[key];//指针要获得地址才能赋值
while(tmp->next!=NULL)
tmp=tmp->next;
tmp->next = new Node(num);
}
len++;
}
//查找hash表中是否有这个元素,有则返回查找次数,无则返回-1
int find(int num){
int sum=;
int key = getKey(num);//获取该数键值
if(hashtable[key].flag==){
insertData(num);
return -;
}
else{
Node *tmp = &hashtable[key];
while(tmp->data!=num){
sum++;
if(tmp->next==NULL){
insertData(num);
return -;
}
tmp=tmp->next;
}
//能退出上面的while说明找到相等的了,return sum
return sum;
}
}
}; int main(){
int n;
cin>>n;
int num[];
for(int i=;i<n;i++)
cin>>num[i]; Hash *hash = new Hash();
for(int i=;i<n;i++)
hash->insertData(num[i]); cin>>n;
for(int i=;i<n;i++){
cin>>num[i];
if(hash->find(num[i])!=-)
cout<<hash->getKey(num[i])<<" "<<hash->find(num[i])<<endl;
else cout<<"error"<<endl;
}

这里待测样例如下:


样例输出:

接下来是用除余法与二次散列再分配写的代码:

//该程序使用二次散列再查找的方式处理冲突构建哈希表
//哈希函数选择除于11的方法 #include<iostream>
using namespace std;
#define modnum 11 struct HashData{
int data;
int flag;
HashData(){
flag=;//当查找次数
}
}; class Hash{ private:
HashData *hashtable;//哈希表
int len;
int *value;
public:
Hash(int n){
hashtable = new HashData[n];
len=n;
int flag = -;
int pos=;
value = new int [len];
for(int i=;i<len/;i++){
value[pos++]=i*i;
value[pos++]=flag*i*i;
}
}
~Hash(){
}
int getKey(int num){
return num%modnum;
}
void insert(int num){
int key = getKey(num); if(hashtable[key].flag==){
hashtable[key].data = num;
hashtable[key].flag = ;
}
else{
int flag1=;
for(int i=;i<len;i++){
//二次散列 ,找到空的位置
flag1++;
//如果此时key+value[i] 是负数就去到H()+len的位置
if(hashtable[(key+value[i]+len)%len].flag==){
hashtable[(key+value[i]+len)%len].data = num;
hashtable[(key+value[i]+len)%len].flag = flag1;
return ;
}
}
}
}
int find(int num){
int key = getKey(num);//获得键值
if(hashtable[key].data==num){
return key+;
}
else{
for(int i=;i<len;i++){
if(hashtable[(key+value[i]+len)%len].data==num){
return (key+value[i]+len)%len+;
}
}
return -;
}
}
int getSum(int num){
int key = find(num);//获得键值
if(key != -)
return hashtable[key-].flag;
else{
int sum=;
for(int i=;i<len;i++){
sum++;
//说明找不到这个元素
if(hashtable[(key+value[i]+len)%len].flag==)
break;
}
return sum;
}
}
void show(){
for(int i=;i<len-;i++){
if(hashtable[i].flag!=){
cout<<hashtable[i].data<<" ";
}
else
cout<<"NULL ";
}
if(hashtable[len-].flag!=){
cout<<hashtable[len-].data<<endl;
}
else
cout<<"NULL"<<endl; for(int i=;i<len;i++)
cout<<hashtable[i].flag<<" ";
cout<<endl;
} void deleteData(int num){
int key = find(num);
if(key!=-){
if(hashtable[key-].flag!=){
hashtable[key-].flag=;
}
}
}
}; int main(){
int n,m;
cin>>m>>n;
Hash *hash = new Hash(m);
int a[];
for(int i=;i<n;i++){
cin>>a[i];
hash->insert(a[i]);
}
hash->show();
//查找
cin>>n;
for(int i=;i<n;i++){
cin>>a[i];
if(hash->find(a[i])!=-){
cout<<<<" "<<hash->getSum(a[i])<<" "<<hash->find(a[i])<<endl;
}
else
cout<<<<" "<<hash->getSum(a[i])<<endl;
}
//删除
cin>>n;
for(int i=;i<n;i++){
cin>>a[i];
hash->deleteData(a[i]);
hash->show();
}
}

样例输入(不包括新加的删除样例):


样例输出:

哈希的就先到这里,那些高端操作拜拜了您下次再看。

DS-哈希表浅析的更多相关文章

  1. 操作系统 之 哈希表 Linux 内核 应用浅析

    1.基本概念         散列表(Hash  table.也叫哈希表).是依据关键码值(Key  value)而直接进行訪问的数据结构. 也就是说,它通过把关键码值映射到表中一个位置来訪问记录.以 ...

  2. 【点分治】【map】【哈希表】hdu4670 Cube number on a tree

    求树上点权积为立方数的路径数. 显然,分解质因数后,若所有的质因子出现的次数都%3==0,则该数是立方数. 于是在模意义下暴力统计即可. 当然,为了不MLE/TLE,我们不能存一个30长度的数组,而要 ...

  3. [PHP内核探索]PHP中的哈希表

    在PHP内核中,其中一个很重要的数据结构就是HashTable.我们常用的数组,在内核中就是用HashTable来实现.那么,PHP的HashTable是怎么实现的呢?最近在看HashTable的数据 ...

  4. Java 哈希表运用-LeetCode 1 Two Sum

    Given an array of integers, find two numbers such that they add up to a specific target number. The ...

  5. ELF Format 笔记(十五)—— 符号哈希表

    ilocker:关注 Android 安全(新手) QQ: 2597294287 符号哈希表用于支援符号表的访问,能够提高符号搜索速度. 下表用于解释该哈希表的组织,但该格式并不属于 ELF 规范. ...

  6. Java基础知识笔记(一:修饰词、向量、哈希表)

    一.Java语言的特点(养成经常查看Java在线帮助文档的习惯) (1)简单性:Java语言是在C和C++计算机语言的基础上进行简化和改进的一种新型计算机语言.它去掉了C和C++最难正确应用的指针和最 ...

  7. 什么叫哈希表(Hash Table)

    散列表(也叫哈希表),是根据关键码值直接进行访问的数据结构,也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度.这个映射函数叫做散列函数,存放记录的数组叫做散列表. - 数据结构 ...

  8. 【哈希表】CodeVs1230元素查找

    一.写在前面 哈希表(Hash Table),又称散列表,是一种可以快速处理插入和查询操作的数据结构.哈希表体现着函数映射的思想,它将数据与其存储位置通过某种函数联系起来,其在查询时的高效性也体现在这 ...

  9. openssl lhash 数据结构哈希表

    哈希表是一种数据结构,通过在记录的存储位置和它的关键字之间建立确定的对应关系,来快速查询表中的数据: openssl lhash.h 为我们提供了哈希表OPENSSL_LHASH 的相关接口,我们可以 ...

随机推荐

  1. Git:将本地项目连接到远程(github、gitee、gitlab)仓库流程

    当进行协同开发或者为了代码安全备份需要,一般都会将本地代码和远程仓库相连接. 备注:Github.Gitee.Gitlab是三个常用的远程git仓库,操作流程基本一致. 提前环境要求: 1.node. ...

  2. 如何在虚拟机VM安装windows 2008R2

    1.首先确保已经安装好VM软件后进行以下操作.首先点击"文件"->再点击新建虚拟机.如图 2.点击新建虚拟机后会弹出如图所示,可以点击"典型",这里建议选 ...

  3. Opencv中KNN背景分割器

    背景分割器BackgroundSubtractor是专门用来视频分析的,会对视频中的每一帧进行"学习",比较,计算阴影,排除检测图像的阴影区域,按照时间推移的方法提高运动分析的结果 ...

  4. break语句、continue语句、goto语句的用法辨析

    1.break语句 break语句常使用在switch语句.循环体以及if语句中,它的作用是跳出循环,而且只能跳出一层循环. for (i = 0; i < 10; ++j) { for (j ...

  5. 08Servlet

    1.Servlet概念 1.1 servlet的特点 1)sevlet是一个普通的java类,继承HttpServlet类. 2)其实实现了Servlet接口的java类,才是一个Servlet类. ...

  6. 树——binary-tree-postorder-traversal(树的后序遍历)

    问题: Given a binary tree, return the postorder traversal of its nodes' values. For example: Given bin ...

  7. 洛谷P3158 [CQOI2011]放棋子 组合数学+DP

    题意:在一个m行n列的棋盘里放一些彩色的棋子,使得每个格子最多放一个棋子,且不同颜色的棋子不能在同一行或者同一列.有多少祌方法? 解法:这道题不会做,太菜了qwq.题解是看洛谷大佬的. 设C是组合数, ...

  8. [USACO07DEC]Sightseeing Cows(负环,0/1分数规划)

    [USACO07DEC]Sightseeing Cows Description Farmer John has decided to reward his cows for their hard w ...

  9. HTTP post get request 时遇到特殊字符 ! @ # $ % ^ & * ( )

    HTTP post get request 时遇到特殊字符 ! @ # $ % ^ & * ( ) 使用对应的编码替代 例如: curl ftp://username:pass@word@ex ...

  10. adb server version (31) doesn’t match this client (36); killing…

    版权声明:蜜蜂采花酿蜂蜜,奶牛吃草产牛奶. https://blog.csdn.net/codehxy/article/details/52175186 案例1 报错信息如下 C:\Users\lin ...