哈希表简介:
哈希表是通过关键值来访问的数据结构,也就是说他通过把关键值映射到表中的一个位置来访问记录,以加快 查找速度,这个映射关系就是哈希函数,存放数据的列表叫做散列表。
首先给出实现哈希表的头文件
#ifndef __HASHTABLE_H__
#define __HASHTABLE_H__
#include<stdio.h>
#include<assert.h>
#include<malloc.h>
#include<string.h>
#include"Common.h"
typedef int DataType;
typedef void (*explore)(int * ,int );
enum State//每个位置都有自己的状态
{
EXIST,//存在的
EMPTY,//可用的
DELETE//被删除的
};
typedef struct KVP
{
DataType value;//存储的值
enum State state;//位置的状态
int order;//顺序
}KVP;
typedef struct HashTable
{
KVP *array;
int size;//有效容量
int capacity;//容量
int total;//已用容量(包括EXIST,DELETE)
explore exp;//函数指针
}HashTable;
//初始化
void InitHashTable(HashTable* ph,explore exp,DataType capacity);
//哈希函数
int HashFunc(int capacity,DataType data);
//插入
void InsertHashTable(HashTable* ph,DataType data);
//打印
void Print(HashTable* ph);
//查找
int Find(HashTable* ph,DataType data);
//删除
void DeleteHashTable(HashTable* ph,DataType data);
//判空
int EmptyHashTable(HashTable* ph);
//有效元素个数
int SizeHashTable(HashTable* ph);
//线性探索
void linear(int* hashaddr,int capacity);
//二次探索
void doub(int* hashaddr,int capacity);
//字符哈希函数
static size_t BKDRHash(const char * str);
//交换数组指针
void swap(HashTable *NewHt,HashTable * ph);
//销毁哈希表
void DestroyHashTable (HashTable * ph);
//增容
void AddCapacity(HashTable *ph);
#endif //__HASHTABLE_H__
要使用哈希表这个结构就要对其结构体进行初始化
下面给出初始化函数
//初始化
void InitHashTable(HashTable* ph,explore exp,DataType capacity)
{
int i=0;
assert(ph);
ph->capacity=capacity;
ph->array=(KVP*)malloc(sizeof(KVP)*ph->capacity);
if(ph->array==NULL)
{
return ;
}
for(;i<ph->capacity;i++)
{
ph->array[i].state=EMPTY;
ph->array[i].order=i;
}
ph->size=0;
ph->total=0;
ph->exp=exp;
}
初始化完成就可以进行插入操作了,但是要插入就有可能容量不够,所以先写出扩容函数
//扩容
void AddCapacity(HashTable *ph)
{
int i=0;
HashTable NewHt;
int NewN;
assert(ph);
if(ph->total*10/ph->capacity>=7)//乘10 是为了避免浮点数的问题,当已用元素个数占用总元素个数的70%以上时
{
NewN=GetNextPrimeNum(ph->capacity);//这个函数的作用是:得到离ph->capacity最近的一个素数,一般比它大
InitHashTable(&NewHt,doub,NewN);
for(;i<ph->capacity;i++)
{
if(ph->array[i].state==EXIST)
{
InsertHashTable(&NewHt,ph->array[i].value);
}
}
swap(&NewHt,ph);
DestroyHashTable (&NewHt);
}
}
下面这是扩容函数用到的得到素数的函数
//得到最近的一个素数
int GetNextPrimeNum(int cur)
{
int i=0;
const int _PrimeSize=28;
static const long _PrimeList [28] =
{
10ul, 97ul, 193ul, 389ul, 769ul,
1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
1610612741ul, 3221225473ul, 4294967291ul
};
for(;i<_PrimeSize;i++)
{
if(_PrimeList[i]>cur)//是否可以相等
{
return _PrimeList[i];
}
}
return _PrimeList [_PrimeSize-1];
}
完成了扩容后,还要通过哈希函数的到关键值,下面给出哈希函数,
int型的哈希函数,采用的是除留余数法,
//int哈希函数
int HashFunc(int capacity,DataType data)
{
return data%capacity;
}
//字符哈希函数
static size_t BKDRHash(const char * str)
{
unsigned int seed = 131; // 31 131 1313 13131 131313
unsigned int hash = 0;
while (*str )
{
hash = hash * seed + (*str++);
}
return hash;
}
好了,现在准备工作都完成了,可以进行哈希表的插入了
//插入
void InsertHashTable(HashTable* ph,DataType data)
{
int hashaddr;
assert(ph);
//判断是否要增容
AddCapacity(ph);
//用哈希函数计算元素位置
hashaddr=HashFunc(ph->capacity,data);
while(EMPTY!=ph->array[hashaddr].state)
{
if(EXIST==ph->array[hashaddr].state)
{
if(data==ph->array[hashaddr].value)
{
return ;
}
}
//线性或者二次探索
ph->exp(&hashaddr,ph->capacity);
}
//出循环肯定是找到可用位置了
ph->array[hashaddr].state=EXIST;
ph->array[hashaddr].value=data;
ph->size++;
ph->total++;
i=0;//每次插入一个都应该将i置0,以便下一次插入
}
插入过程中还用到了,在 初始化哈希表时就完成的函数指针赋值,这里的函数为了解决哈希冲突以及哈希冲突导致的冲突堆积儿设计 的,共有两个 线性探测和二次探测
//线性探索
void linear(int* hashaddr,int capacity)
{
assert(hashaddr);
(*hashaddr)++;
if(*hashaddr==capacity)
{
*hashaddr=0;
}
}
//二次探索
void doub(int* hashaddr,int capacity)
{
assert(hashaddr);
*hashaddr=*hashaddr+i*i+1;
i++;//是一个全局变量,但是我在每次插入完成后都会置i为0
if(*hashaddr>=capacity)
{
*hashaddr%=capacity;
}
}
下面是在哈希表中进行查找的函数
//查找
int Find(HashTable* ph,DataType data)
{
int hashaddr;
int startaddr;
assert(ph);
//用哈希函数计算元素位置
hashaddr=HashFunc(ph->capacity,data);
startaddr=hashaddr;//保存位置
while(EMPTY!=ph->array[hashaddr].state)
{
if(EXIST==ph->array[hashaddr].state)
{
if(data==ph->array[hashaddr].value)
{
return hashaddr;
}
}
//线性或者二次探索
ph->exp(&hashaddr,ph->capacity);
//这是线性的判断方式
if(hashaddr==startaddr)//找了 一圈 都没有找到
{
return -1;
}
//二次判断找不到的方式
//计算哈希因子,大于0.7就结束
}
return -1;
}
//删除
void DeleteHashTable(HashTable* ph,DataType data)
{
int ret;
assert(ph);
ret=Find(ph,data);
if(-1==ret)
{
return ;
}
ph->array[ret].state=DELETE;
ph->size--;
}
//判空
int EmptyHashTable(HashTable* ph)
{
assert(ph);
return ph->total==0;
}
//有效元素个数
int SizeHashTable(HashTable* ph)
{
return ph->size;
}