[c语言实现]哈希表

我们在这篇博客 哈希表初探
已经初步了解了哈希表的作用,那么接下来就应该自己实现一下哈希表了.

同样的,实现两种不同的解决哈希冲突的方案1,闭散列 2,哈希桶

闭散列

头文件

#pragma once
#include<stdio.h>
//这个结构体表示哈希表中的一个元素
//这个元素中同时包含了键值对
typedef int KeyType;
typedef char ValType;

typedef enum State
{

  Empty, //空状态
  Vaild,//有效状态
  Deleted,//服务于remove
}State;
struct HashTable;
typedef size_t (*HashFunc)(KeyType key,struct HashTable* );

typedef struct HashElem{
  KeyType key;
  ValType val;
//每个hashtable内元素的状态``
  State state;
}HashElem;
typedef struct HashTable{
  HashElem* data;
  size_t size;
  size_t capacity;
  // 填装因子
  float Load_Factor;
    //哈希函数
  HashFunc func;
}HashTable;
void HashInit(HashTable* ht,HashFunc hash_func);
void HashDestory(HashTable* ht);
void HashInsert(HashTable* ht,HashElem elem);
ValType HashFind(HashTable* ht,  KeyType key);
size_t hash_func( KeyType key,HashTable*ht);
void  HashRemove(HashTable* ht,  KeyType key);
void HashPrint(HashTable* ht);

各个函数的实现

void HashInit(HashTable* ht,HashFunc hash_func);

初始化哈希表的工作很简单,分配存储哈希元素的数组,然后初始化 哈希表内各个成员的初始值,
值得注意的是我们在申请哈希表空间之后,数组内的元素是随机值,我们要标识该元素是否有效,那么得把数组内每个元素的状态都至为Empty,(简单的把元素都至为0是不行的,万一要插入的key是0,那么会出现此位置已经被占用的错误)

size_t GetHashTableCapacity(HashTable* ht)
{
  if(ht  == NULL)
    return -1;
  return ht->capacity;
}
size_t hash_func( KeyType key,HashTable*ht)
{
  return key % GetHashTableCapacity(ht); 
}

void HashInit(HashTable* ht,HashFunc hash_func)
{
  if(ht == NULL || hash_func == NULL)
    return;
  ht->capacity = 1000;
  ht->data= (HashElem*) malloc(sizeof(size_t)* ht->capacity);
  ht->size = 0;
  ht->func = hash_func;
  size_t i = 0;
  for( ; i < ht->size; i++ )
  {
    ht->data[i].state  = Empty;
  }
}

void HasDestory(HashTable* ht)

销毁很简单,不提


void HashInsert(HashTable* ht,HashElem elem)

在哈希表内插入元素,需要考虑的问题有
1.哈希表满了,2插入的key已经存在,3哈希冲突的问题

void HashInsert(HashTable* ht,HashElem elem)
{
  KeyType key = elem.key;
  if(ht == NULL)
    return;
  if(ht->size >= ht->capacity * ht->Load_Factor)
  {
      //哈希表已经满了
      return;
  }
  size_t offset = (*ht->func)(key,ht);
    //哈希冲突
  while(ht->data[offset].state == Vaild  )
  {
    offset++;
    //在占用中找到了重复元素
    if(ht->data[offset].key == key)
    {
      //重复元素
      printf("重复元素\n");
      return;

    }
      //从头开始找
    if( offset == ht->capacity )
    {
      //有填充因子存在 不会死循环
      offset = 0;
    }
  }


  //找到第一个状态不是 已经占用的位置插入
  ht->data[offset] = elem;`
  ht->data[offset].state = Vaild;
  ht->size++;
}

ValType HashFind(HashTable* ht,  KeyType key)
void  HashRemove(HashTable* ht,  KeyType key)

查找要注意的点是:哈希冲突,往后如果找到empty还没找到key,就表明key不在哈希表中,

因为这一点,当我们删除时不能将key的状态置为empty,这样会使再次查找key以后的元素失败,所以我们应该为删除元素独立设立一个状态Deleted

ValType HashFind(HashTable* ht,  KeyType key)
{
  if(ht == NULL)
    return -1;
  size_t offset = (*hash_func)(key,ht);
  //不为空但是又不是 key offset ++
  while(1)
  {
    //碰到查找的元素是delete状态
    //其实可以忽略,往后找空的就行
    if(ht->data[offset].state == Empty)
    {
      printf("not found \n");
      return -1;
    }
    //排除delete状态
    if(ht->data[offset].key == key && ht->data[offset].state == Vaild ) 
    {
     return ht->data[offset].val; 
    }
    offset++;
    if(offset == ht->capacity)
      offset = 0;
  }
  //遇到删除的状态还得往后找(删除后面元素的状态可能是vaild)
  //只有遇到空状态才表示查找失败(空表示此位置肯定没有进行插入,它后面的元素就不可能是哈希冲突造成的vaild状态)
}

void  HashRemove(HashTable* ht,  KeyType key)
{
  if(ht == NULL)
    return ;
  size_t offset = (*hash_func)(key, ht);
  while(ht->data[offset].state != Empty)
  {
    if(ht->data[offset].key == key && ht->data[offset].state == Vaild)
    {
      ht->data[offset].state = Deleted;
      return;
    }
    offset++;
    if(offset == ht->capacity)
    {
      offset = 0;
    }
  }
  printf("not found \n");
  return;
}
void HashPrint(HashTable* ht)
{
  if(ht == NULL)
    return;
  size_t i = 0;
  for( ;  i < ht->capacity; i++ )
  {
    if(ht->data[i].state != Vaild)
    {
      continue;
    }
    printf("[%lu|%d|%d] " ,i, ht->data[i].key, ht->data[i].val);
  }
  printf("\n");
}

哈希桶


#pragma once
#include<stdio.h>
#define HashMaxSize 1000
typedef int KeyType;
typedef char ValType;
typedef size_t (*HashFunc)(KeyType key );
typedef struct HashEelem{
  KeyType key;
  ValType val;
  struct HashEelem* next;
}HashEelem;
typedef struct HashTable{
  HashEelem* data[HashMaxSize];
  size_t size;
  //哈希桶不需要填装因子和状态枚举

  HashFunc func;
}HashTable;
size_t hash_func( KeyType key);
void HashInit(HashTable* ht,HashFunc hash_func);
void HashDestory(HashTable* ht);
void HashInsert(HashTable* ht,HashEelem elem);
ValType HashFind(HashTable* ht,  KeyType key);
size_t hash_func( KeyType key);
void  HashRemove(HashTable* ht,  KeyType key);
void HashPrint(HashTable* ht);
#include"hash.h"
#include<stdlib.h>
#include<unistd.h>
size_t hash_func( KeyType key)
{
  return key % HashMaxSize; 
}
void HashInit(HashTable* ht,HashFunc hash_func)
{
  if(ht == NULL)
    return;
  int i = 0;
  for( ; i < HashMaxSize; i++ )
  {
    ht->data[i] = NULL;
  }
  ht->size = 0;
  ht->func = hash_func;
}
void HashDestory(HashTable* ht)
{
  if(ht == NULL)
    return;
  size_t i = 0;
  for(; i < HashMaxSize; i++)
  {
    HashEelem* cur = ht->data[i];
    HashEelem* to_delete = NULL;
    while(cur)
    {
      to_delete = cur;
      cur= cur->next;
      free(to_delete);
      to_delete = NULL;
    }
  }
 ht->size = 0; 
}
HashEelem* CreateNode(KeyType key, ValType val)
{
   HashEelem* elem =    (HashEelem*)malloc(sizeof(HashEelem));
   if(elem == NULL)
     return NULL;
   elem->key = key;
   elem->val = val;
   elem->next = NULL;
   return elem;
}
void HashInsert(HashTable* ht,HashEelem elem)
{
  if(ht == NULL)
    return;
  size_t offset = ht->func(elem.key);
  if(ht->data[offset] == NULL)
  {
    ht->data[offset] = CreateNode(elem.key,elem.val);
  }
  else 
  {
    HashEelem* new_node= CreateNode(elem.key,elem.val);
    new_node->next = ht->data[offset];
    ht->data[offset] = new_node;
  }
  ht->size++;
}
ValType HashFind(HashTable* ht,  KeyType key)
{
  if(ht == NULL)
    return -1;
  size_t offset = ht->func(key);
  HashEelem* cur = ht->data[offset];
  while(cur)
  {
   if(cur->key == key)
   {
     return cur->val;
   }
   cur = cur->next;
  }
  printf("not found\n");
  return -1;
}
void  HashRemove(HashTable* ht,  KeyType key)
{
  if(ht == NULL)
    return;
  size_t offset = ht->func(key);
  HashEelem* cur = ht->data[offset];
  HashEelem* pre_cur = NULL;
  while(cur)
  {
    if( cur->key == key )
    {
      break;
    } 
    pre_cur = cur;
    cur = cur->next;
  }
  if(cur == NULL)
  {
    printf("empty key \n");
  }
  else if(pre_cur == NULL)
  {
    free(cur);
    cur = NULL;
  }
  else 
  {
    free(cur);
    cur = NULL;
   pre_cur->next = NULL; 
  }
}
void HashPrint(HashTable* ht)
{
  size_t i = 0;
  for(; i < HashMaxSize; i++)
  {
    if( ht->data[i] == NULL )
      continue;
    HashEelem* cur = ht->data[i] ;
    printf("第%lu条链表结构如下\n",i); 
    while(cur)
    {
      /*sleep(1);*/
      printf("[key: %d| val: %d] ", cur->key,cur->val);
      cur = cur->next;
    }
    printf("\n");
  }
  printf("\n");
}

猜你喜欢

转载自blog.csdn.net/kwinway/article/details/80429626
今日推荐