1. 程式人生 > >[c語言實現]雜湊表

[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");
}