目录
引例题目(来源于PTA):
一. 哈希表的建立
1. 基本概念
哈希法又称散列法,即通过哈希函数来快速定位元素位置,用于存取元素,由此建立起来的存储表称为哈希表。
2. 构建方法
(1)数字分析法:取元素各位数字上分布较为均匀的数字位组合为存取地址。若各关键字中d4和d7的取值分布较均匀,则哈希函数为:h(key)=h(d1d2d3…d7d8)=d4d7;例如 h(81346532)=43,h(81301367)=06;
(2)平方取中法:数字不处理时数字分布不均匀,可以平方后再数字分析;
(3)分段叠加法:将数字分割为位数相等的几部分,重叠或者移位相加,和舍去高位结果为地址值;
(4)除留余数法:h(k) = k % p;
二. 哈希冲突
哈希函数对于不同的元素可能计算出的地址是一样的,这就产生了哈希冲突,所以我们要处理哈希冲突。
1. 线性探测法
线性探测法的思想是在冲突位置后面寻找不冲突的位置插入,查找时亦然。其实现代码如下:
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
typedef int ElemType;
typedef enum { Legitimate, Empty, Deleted } EntryType;
struct HashEntry{//节点
ElemType Data;
EntryType Info;
};
typedef HashEntry Cell;
struct TblNode{//哈希表
int TableSize;//表长
Cell *cells;//存储节点数组
};
typedef struct TblNode *HashTable;
int Hash(ElemType key,int sized){//哈希函数
return key%sized;
}
int Find( HashTable H, ElemType key ){//查找函数(返回元素位置或者应该插入的位置)
int pos = Hash(key,H->TableSize);
for(int i = 0;i<H->TableSize;i++){
int p = (pos + i)%H->TableSize;//!
if(H->cells[p].Data==key||H->cells[p].Info==Empty)return p;
}
return -1;
}
void Insert(HashTable H,ElemType key){//插入函数
int pos = Find(H,key);
if(pos==-1){printf("Insert Error\n");return;}
for(int i = 0;i<H->TableSize;i++){
int p = (pos + i)%H->TableSize;
if(H->cells[p].Info==Empty){
H->cells[p].Data = key;
H->cells[p].Info = Legitimate;
return;
}
}
}
HashTable BuildTable(){//建立哈希表
HashTable HB = (HashTable)malloc(sizeof(TblNode));
scanf("%d",&HB->TableSize);
HB->cells = (Cell*)malloc(sizeof(Cell)*HB->TableSize);
for(int i = 0;i<HB->TableSize;i++)HB->cells[i].Info = Empty;//初始化
for(int i = 0;i<HB->TableSize;i++){
ElemType num;
scanf("%d",&num);
Insert(HB,num);//插入元素
}
return HB;
}
int main()
{
HashTable H = BuildTable();
for(int i = 0;i<H->TableSize;i++){
if(i!=0)printf(" ");
if(H->cells[i].Info==Empty||H->cells[i].Info==Deleted)printf("null");
else printf("%d",H->cells[i].Data);
}
printf("\n");
ElemType key;
scanf("%d",&key);
int pos = Find(H,key);
if (pos==-1)
printf("ERROR: %d is not found and the table is full.\n", key);
else if (H->cells[pos].Info == Legitimate)
printf("%d is at position %d.\n", key, pos);
else
printf("%d is not found. Position %d is returned.\n", key, pos);
return 0;
}
2. 分离链接法
分离链接法又称为链地址法,其思想是把产生冲突的元素都放到一个集合里,查找时在集合中查找即可。其实现代码如下
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
typedef int ElemType;
struct LNode{//节点
ElemType Data;
LNode *next;
};
typedef LNode *List;
struct TblNode{//哈希表
int TableSize;//表长
List Heads;//各地址集合的头节点
};
typedef TblNode *HashTable;
int Hash(ElemType key,int Size){//哈希函数
return key%Size;
}
HashTable InitTable(){//初始化表
HashTable HB = (HashTable)malloc(sizeof(TblNode));
scanf("%d",&HB->TableSize);
HB->Heads = (List)malloc(sizeof(LNode)*HB->TableSize);
for(int i = 0;i<HB->TableSize;i++){
HB->Heads[i].next = NULL;
}
return HB;
}
List Find(HashTable H,int key){//查找函数(返回位置指针/空指针)
int pos = Hash(key,H->TableSize);
List p = H->Heads[pos].next;
while(p!=NULL){
if(p->Data==key)return p;
p = p->next;
}
return p;
}
void Insert(HashTable H,ElemType key){//插入函数
List p = Find(H,key);
if(p==NULL){//没有该元素
int pos = Hash(key,H->TableSize);
List L = (List)malloc(sizeof(LNode));
L->Data = key;//头插法
L->next = H->Heads[pos].next;
H->Heads[pos].next = L;
}
else{//已存在
printf("Insert Error! Exit\n");
}
}
void BuildTable(HashTable H){//建立哈希表
for(int i = 0;i<H->TableSize;i++){
ElemType num;
scanf("%d",&num);
Insert(H,num);
}
}
bool Delete( HashTable H, ElemType key ){//删除元素
List p = Find(H,key);
if(p==NULL)return false;
else{
int pos = Hash(key,H->TableSize);
List L = &H->Heads[pos];
while(L->next->Data!=key){
L = L->next;
}
LNode *q = L->next;
L->next = q->next;
q->next = NULL;
free(q);
printf("%d is deleted from list Heads[%d]\n",key,pos);
}
return true;
}
int main()
{
HashTable H = InitTable();
BuildTable(H);
ElemType Key;
int T;
scanf("%d",&T);
while(T--){
scanf("%d",&Key);
if (Delete(H, Key) == false)
printf("ERROR: %d is not found\n", Key);
}
return 0;
}