这是一类用于查找到数据结构
哈希函数
我们先来想一下,在用数组储存时,如果我们知道下标我们是不是直接就可以查找到这个数据,即时间复杂度是O(1).
假设有一个整型数组,我们将数字m映射到n处,如果我们想查找m是不是在数组中,我们可以将m映射成为n,去下标位n的地方查找。这个映射方法就是一个哈希函数,推广就是把一个任意类型的元素(即在任意类型的数组中)映射成一个只能为整型的下标。
举个例子:
14 |
0 1 2 3 4 5 6 7 8 9
设这个哈希函数是一个值对数组长度的运算法则。设值为14,那么它就存在下标为14%9=5处.但是假设我们还想存储一个数字为5,我们会发现存不下了,那么思考一下,数字5应该怎么存储?
这个时候我们来介绍冲突处理方法,以处理这种问题。
冲突处理
其实冲突处理方法一种是拉链法,一种是开放定址法。
最容易理解的方法,就是往后找,如果5映射到数组上下标应该是5,而下标是5 的地方已经有数据了,那我们判断下标为6的地方是否有数据,如果没有就把5放到下标为6处,否则继续判断下一位。
14 | 5 |
0 1 2 3 4 5 6 7 8 9
在开放定址法里面会有线性探测法,即我们刚刚说的方法,还有就是平方探测法,以刚刚的例子就是如果下标为5处有数据,但是我们五按照我们的哈希函数法则应该储存于下表为5处,我们就把应该存入的下标+1^2=+1即6处,如果下标为6处有数据,我们就进行一个-1^2=-1的操作即存入下标为4处,如果没有数据就存入如果没有进行相似操作+2^2,-2^2,+3^2,-3^2……
代码实现
对于代码演示我们用拉链法解决冲突
代码如下:
#include<bits/stdc++.h>
#include<stdlib.h>
#include<string.h>
//最重要两部分是冲突处理方法和哈希函数
using namespace std;
typedef long long ll;
typedef struct Node{
char *str;//假设哈希我们要存入字符串
struct Node *next;//拉链法 ,下一个节点
}Node;
typedef struct HashTable{
Node**data;//存储若干链表头地址的数组
//链表头地址是Node*
int size;//哈希表大小
}HashTable;
//int Ha
Node *init_node(char*str,Node*head){//使用链表的头插法
Node *p=(Node*)malloc(sizeof(Node));
//Node*p;
p->str=strdup(str);
//strdup将串拷贝到新建位置处,返回值是一个指向复制字符串分配空间的指针
p->next=head;
return p;
}
HashTable *init_hashtable(int n){//存n个元素的哈希表
HashTable*h=(HashTable*)malloc(sizeof(HashTable));
h->size=n*2;//开大两倍
//h->size=n<<1;
h->data=(Node**)calloc(sizeof(Node*),h->size);//因为要初始化
return h;
}
int BKDRHash(char*str){//哈希函数
int seed=31;
int hash=0;
for(int i=0;str[i];i++){
hash=hash*seed+str[i];
}
return hash & 0x7fffffff;//保证返回的是一个正值
}
//哈希表的插入
int insert(HashTable*h,char*str){
int hash=BKDRHash(str);
int ind=hash%h->size;//转化成数组下标
h->data[ind]=init_node(str,h->data[ind]);//头插法
return 1;
}
//如果用开放定址法 我们会发现拉链法要简单
/*int insert(HashTable*h,char*str){
int hash=BKDRHash(str),times=0;
int ind=has%h->size;
Node*node=init_node(str,NULL);
while(h->data[ind]){
times++;
ind+=times*times;
ind%=h->size;
}
h->data[ind]=node;
}*/
int search(HashTable*h,char*str){
int hash=BKDRHash(str);
int ind=hash%h->size;
Node*p=h->data[ind];
while(p && strcmp(p->str,str)){
p=p->next;
}
return p!=NULL;
}
//哈希表的clear
void clear_node(Node*node)
{
if(node==NULL)return;
Node*p,*q;
p=node;//
while(p!=NULL){
q=p->next;
//free(p->next);
free(p->str);
free(p);
p=q;
}
}
void clear_HashTable(HashTable*h){
if(h==NULL){
return;
}
for(int i=0;i<h->size;i++){
clear_node(h->data[i]);
}
free(h->data);
free(h);
return;
}
int main()
{
//cout<<"(((";
//char *str;
char str[100];
int op;
HashTable*h=init_hashtable(10);
while(scanf("%d %s",&op,str)!=EOF){//建议不要用cin ,咱也不知道为啥,因为它运行不对
//cout<<"ss";cin>>op>>str
if(op%2==0){//插入操作
cout<<"insert "<<str<<" to hash table"<<endl;
insert(h,str);
}
else{
cout<<"search:"<<str<<" result:"<<search(h,str)<<endl;
}
}
return 0;
}
总结
虽然先看了网课,但这个代码演示对之后真的非常快乐,因为改了好多次,有很多的错误。写博客最主要还是为了加深自己对于算法和数据结构的理解,完成之后真的很快乐啊