数据结构之哈希搜索结构(一)

什么是哈希搜索结构

顺序搜索以及二叉搜索树中,元素存储位置和元素各关键码之间没有对应的关系,因此在查找一个元素的时候,必须要经过关键码的多次比较。所以这样的搜索效率取决于搜索过程中元素的比较次数。

而我们理想的搜索方法就是不用经过任何比较,一次直接从表中得到要搜索的元素。如果能够构造一种结构,让所要查找的关键码与它的存储位置有一定的关系,那么可以根据这种关系找到其位置从而更快的找到关键码。

在这种结构中插入时,根据待插入的元素从而经过某些函数计算出它的插入位置,进行插入;查找时通过对查找元素的计算出其位置,再根据位置在结构中的对应出进行比较,如果相等则查找成功。
这种方式的结构就是哈希搜索结构,其中使用的计算位置函数就是哈希函数。

哈希冲突

在说哈希冲突之前,首先我们先定义一个简单的哈希函数方便我们解释。

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

欢迎大家共同讨论,如有错误及时联系作者指出,并改正。谢谢大家!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值