數據結構之哈希搜索結構(一)

什麼是哈希搜索結構

順序搜索以及二叉搜索樹中,元素存儲位置和元素各關鍵碼之間沒有對應的關係,因此在查找一個元素的時候,必須要經過關鍵碼的多次比較。所以這樣的搜索效率取決於搜索過程中元素的比較次數。

而我們理想的搜索方法就是不用經過任何比較,一次直接從表中得到要搜索的元素。如果能夠構造一種結構,讓所要查找的關鍵碼與它的存儲位置有一定的關係,那麼可以根據這種關係找到其位置從而更快的找到關鍵碼。

在這種結構中插入時,根據待插入的元素從而經過某些函數計算出它的插入位置,進行插入;查找時通過對查找元素的計算出其位置,再根據位置在結構中的對應出進行比較,如果相等則查找成功。
這種方式的結構就是哈希搜索結構,其中使用的計算位置函數就是哈希函數。

哈希衝突

在說哈希衝突之前,首先我們先定義一個簡單的哈希函數方便我們解釋。

int HashFun(int key)
{
    return key % 1000;//關鍵碼爲key,其存儲對應位置爲key模1000
}

這時候我們就可以建立起關鍵碼與其存儲位置之間的關係了。
這裏寫圖片描述
這裏寫圖片描述

這個時候的1001應該插入到什麼位置呢?這種情況的出現就是哈希衝突。

解決哈希衝突常見的有兩種方法:閉散列和開散列。

負載因子

爲了增加哈希表的搜索效率,儘量不讓哈希表插滿,而是規定一個負載因子,控制哈希表內插入元素的個數,從而提高效率。
負載因子的定義爲:α = 填入表中元素 / 可填入總元素
α 越大,表明填入表內元素越多,產生哈希衝突的可能就越大。相反則越小。

閉散列

閉散列解決哈希衝突的方式是,當發生哈希衝突時,如果哈希表未放滿,則哈希表內必定有空餘位置,那麼此時將衝突的關鍵碼放置表中的下一個空位去。
這裏寫圖片描述

接下來來,我們實現一下閉散列哈希表的基本操作。

實現

//hash.h

#pragma once

#include <stddef.h>

#define HASHMAXSIZE 1000//總個數

typedef int Keytype;
typedef int Valtype;

typedef size_t (*HashFun)(Keytype key);

typedef enum Stat {
  Empty,//空
  Valid,//有效
  Delete//已刪除
} Stat;//標誌位

typedef struct HashElem {
  Keytype key;//關鍵碼
  Valtype val;//data
  Stat stat;
} HashElem;

typedef struct HashTable {
  HashElem data[HASHMAXSIZE];
  size_t size;//元素個數
  HashFun fun;//哈希函數
} HashTable;

void HashInit(HashTable* ht, HashFun fun);//初始化哈希表

void HashInsert(HashTable* ht, Keytype key, Valtype val);//插入哈希表

void HashDestroy(HashTable* ht);//銷燬哈希表

void HashRemove(HashTable* ht, Keytype key);//刪除哈希表指定元素

int HashFind(HashTable* ht, Keytype key, Valtype* val);//查找哈希表內元素
//hash.c

#include "hash.h"
#include <stdio.h>

size_t Function(Keytype key) {
  return key % HASHMAXSIZE;
}

void HashInit(HashTable* ht, HashFun fun)//初始化哈希表
{
  if(ht == NULL) {
    return;
  }
  ht->size = 0;
  ht->fun = fun;

  size_t i = 0;
  for(; i < HASHMAXSIZE; ++i) {
    ht->data[i].stat = Empty;
  }
  return;
}

void HashInsert(HashTable* ht, Keytype key, Valtype val)//插入哈希表
{
  if(ht == NULL) {
    return;
  }
  if(ht->size > 0.8 * HASHMAXSIZE) {//負載因子
    return;
  }
  size_t offset = ht->fun(key);
  while(1) {
    if(ht->data[offset].key == key && ht->data[offset].stat == Valid) {
      //如果關鍵值key相同,那麼就插入失敗
      return;
    }
    if(ht->data[offset].stat != Valid) {
      ht->data[offset].key = key;
      ht->data[offset].val = val;
      ht->data[offset].stat = Valid;
      break;
    }
    ++offset;
    if(offset >= HASHMAXSIZE) {
      offset = 0;
    }
  }
  ++ht->size;
  return;
}

void HashDestroy(HashTable* ht)//銷燬哈希表
{
  if(ht == NULL) {
    return;
  }
  ht->size = 0;
  ht->fun = NULL;

  size_t i = 0;
  for(; i < HASHMAXSIZE; ++i) {
    ht->data[i].stat = Empty;
  }
  return;
}

void HashRemove(HashTable* ht, Keytype key)//刪除哈希表指定元素
{
  if(ht == NULL) {
    return;
  }
  if(ht->size == 0) {
    return;
  }
  size_t offset = ht->fun(key);
  while(1) {
    if(ht->data[offset].stat != Valid) {
      break;
    }
    if(ht->data[offset].key == key) {
      ht->data[offset].stat = Delete;//改變狀態
      --ht->size;
      break;
    }
    ++offset;
    if(offset >= HASHMAXSIZE) {
      offset = 0;
    }
  }
  return;
}

int HashFind(HashTable* ht, Keytype key, Valtype* val)//查找哈希表內元素
{
  if(ht == NULL) {
    return 0;
  }
  if(ht->size == 0) {
    return 0;
  }

  size_t offset = ht->fun(key);
  while(1) {
    if(ht->data[offset].stat != Valid) {
      return 0;
    }
    if(ht->data[offset].key == key) {
      ht->data[offset].stat = Delete;
      --ht->size;
      break;
    }
    ++offset;
    if(offset >= HASHMAXSIZE) {
      offset = 0;
    }
  }
  *val = ht->data[offset].val;
  return 1;
}

歡迎大家共同討論,如有錯誤及時聯繫作者指出,並改正。謝謝大家!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章