【cpp|哈希表(散列表)】1.为什么有哈希表?
因为哈希表的优点:常数时间的插入、查找、删除操作;调节内存和空间,结合了数组和链表的优点;以“键值对”存储数据;
//线性探测
#include
2 #include
3
4 #define SUCCESS (int)1
5 #define UNSUCCESS (int)0
6 #define HASHSIZE 12 /* 哈希表长度 */
7 #define NULLKEY -32768
8
9 typedef struct HashTable
10 {
11int *elem;
/* 哈希表元素存储地址,动态分配数组 */
12int count;
/* 当前数据元素的个数 */
13 }HashTable;
14
15 int m=0;
16
17 /* 初始化哈希表 */
18 int InitHashTable(HashTable *H)
19 {
20int i;
21m=HASHSIZE;
22H->count=m;
23H->elem=(int*)malloc(m*sizeof(int));
24for(i=0;
ielem[i]=NULLKEY;
26}
27return SUCCESS;
28 }
29
30 /* 哈希函数 */
31 int Hash(int key)
32 {
33return key%m;
34 }
35
36 /* 将关键字插入散列函数 */
37 void InsertHash(HashTable *H,int key)
38 {
39int addr=Hash(key);
/* 求插入key的哈希地址 */
40while(H->elem[addr]!=NULLKEY){
41addr=(addr+1)%m;
/* 如果不为空,发生冲突, 线性探测 */
42}
43H->elem[addr]=key;
/* 直到有空位后插入关键字 */
44 }
45
46/* 查找 */
47 int SearchHash(HashTable H,int key, int *addr)
48 {
49*addr=Hash(key);
50while(H.elem[*addr]!=key){ /* 如果H[*addr]不等于key, 则冲突,线性探测 */
51*addr=(*addr+1)%m;
52if(H.elem[*addr]=NULLKEY || *addr==Hash(key)){ /* 找不到或循环回到原点 */
53return UNSUCCESS;
54}
55}
56return SUCCESS;
57 }
//平方探测
#include
#include
#include
#include
#include
using namespace std;
#define MAX_TABLESIZE 10000 /* 允许开辟最大散列表长度 */
typedef int ElementType;
enum EntryType{Legitimate,Empty,Deleted};
/* 散列单元的状态类型 */
typedef struct HashEntry
{
ElementType data;
//存放元素
enum EnTryType state;
//单元状态
};
typedef struct HashEntry Cell;
typedef struct TblNode
{
int tablesize;
//表的最大长度
Cell *cell;
//存放散列单元数据的数据
}TblNode,*HashTable;
/* 返回大于n且不超过MAXTABLESIZE的最小素数 */
int NextPrime(int n)
{
int p=(n%2)?n+2:n+1;
// 大于n的第一个奇数
int i;
while(p<=MAX_TABLESIZE){
for(i=(int)sqrt(p);
i>2;
i--){
if(p%i==0) break;
}
if(i==2){
break;
}else{
p+=2;
}
}
return p;
}/* 创建哈希表 */
HashTable CreatTable(int table_size)
{
HashTable h=(HashTable)malloc(sizeof(TblNode));
h->tablesize=NextPrime(table_size);
h->cell=(Cell*)malloc(h->tablesize*sizeof(Cell));
for(int i=0;
itablesize;
i++){
h->cell[i].state=Empty;
}
return h;
}/* 查找数据的初始位置,取余法确定散列函数 */
int Hash(ElementType key)
{
return key%11;
//除留取余法
}/* 如果key不在Hash表中,函数也会返回一个地址,这是不应该的;
那该怎么处理呢?
* 不对不对,这个函数查找key是否已经在表中被占用,而是说key在Hash表中应该有
* 一席之地,即一定有位置可以存储key
* */
int Find(HashTable h,ElementType key)
{
int current_pos, new_pos;
int collision_num=0;
//记录冲突次数
new_pos=current_pos=Hash(key);
while(h->cell[new_pos].state!=Empty&&h->cell[new_pos].data!=key){
collision_num++;
if(collision_num%2){ //处理奇数冲突
new_pos=current_pos+(collision_num+1)*(collision_num+1)/4;
if(new_pos>=h->tablesize){
new_pos=new_pos%h->tablesize;
}
}else{ //处理偶数冲突
new_pos=current_pos-(collision_num)*(collision_num)/4;
while(new_pos<0){
new_pos+=h->tablesize;
}
}
}
return new_pos;
}bool Insert(HashTable h,ElementType key)
{
int pos=Find(h,key);
if(h->cell[pos].state!=Legitimate){
h->cell[pos].state=Legitimate;
h->cell[pos].data=https://www.it610.com/article/key;
return true;
}else{
cout<<"key已经被占用"<
//链地址
#include
#include
#include
#include
#include
using namespace std;
#define MAXTABLE_SIZE 10000 //允许最大的散列长度
#define KEYLENGTH 100 //允许关键字最大长度typedef int ElementType;
struct LNode
{
ElementType data;
LNode *next;
};
//链表节点
typedef struct LNode *PtrToNode;
typedef PtrToNode LinkList;
struct TblNode
{
int tablesize;
LinkList heads;
};
//哈希表Key表
typedef struct TblNode *HashTable;
int NextPrime(int n)
{
int p=(n%2)?n+2:n+1;
int i;
while(p<=MAXTABLE_SIZE){
for(i=(int)sqrt(p);
i>2;
i--){
if(p%i==0){
break;
}
}
if(i==2){
break;
}else{
p+=2;
}
}
return p;
}
HashTable CreateTable(int table_size)
{
HashTable h=(HashTable)malloc(sizeof(TblNode));
h->tablesize=NextPrime(table_size);
h->heads=(LinkList)malloc(h->tablesize*sizeof(LNode));
for(int i=0;
itablesize;
i++){
h->heads[i].next=NULL;
}
return h;
}int Hash(ElementType key, int n)
{
return key%11;
}//找到pos=Hash(key), 在找到h->heads[pos]中找到key
LinkList Find(HashTable h,ElementType key)
{
int pos;
pos=Hash(key,h->tablesize);
LinkList p=h->heads[pos].next;
while(p&&key!=p->data){
p=p->next;
}
return p;
}bool Insert(HashTable h,ElementType key)
{
LinkList p=Find(h,key);
if(!p){
LinkList new_link=(LinkList)malloc(sizeof(LNode));
new_link->data=https://www.it610.com/article/key;
int pos=Hash(key,h->tablesize);
new_link->next=h->heads[pos].next;
h->heads[pos].next=new_link;
//哑头链表,pos冲突的key值插入到链表头部
return true;
}else
{
cout<<"键值已经存在"<heads[i].next;
while(p){
temp=p->next;
free(p);
p=temp;
}
}
free(h->heads);
free(h);
}int main(int argc,char *argv[])
{
int a[]={47,7,29,29,11,16,92,22,8,3,50,37,89,94,21};
int n=15;
HashTable h=CreateTable(n);
for(int i=0;
iheads[i].next;
while(p){
coutnext;
}
cout<
参考链接:https://blog.csdn.net/weixin_38169413/article/details/81612307
推荐阅读
- 笔记|C语言数据结构——二叉树的顺序存储和二叉树的遍历
- C语言学习(bit)|16.C语言进阶——深度剖析数据在内存中的存储
- 数据结构和算法|LeetCode 的正确使用方式
- 先序遍历 中序遍历 后序遍历 层序遍历
- 数据结构|C++技巧(用class类实现链表)
- 数据结构|贪吃蛇代码--c语言版 visual c++6.0打开
- 算法|算法-二分查找
- 数据结构学习指导|数据结构初阶(线性表)
- leetcode题解|leetcode#106. 从中序与后序遍历序列构造二叉树
- java|ObjectOrientedProgramming - 面向对象的编程(多态、抽象类、接口)- Java - 细节狂魔