哈希表和哈希桶

哈希表简介:

      哈希表是通过关键值来访问的数据结构,也就是说他通过把关键值映射到表中的一个位置来访问记录,以加快 查找速度,这个映射关系就是哈希函数,存放数据的列表叫做散列表。





首先给出实现哈希表的头文件

#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;
}





猜你喜欢

转载自blog.csdn.net/wm12345645/article/details/80329944