【数据结构】哈希表的基本操作(一)·线性探测法解决哈希冲突

今天我们主要的是用线性探测的方法处理哈希冲突的问题。
线性探测方法的具体实现如下:

test.h

#pragma once
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>

//我们在这里的hash表期望存储的数据是键值对这样的结构

#define HashMaxSize 1000

typedef enum Stat{
    empty,//空状态
    valid,//有效状态
    deleted,//删除状态
}Stat;

typedef int Hashtype;
typedef  int keytype;
typedef int valtype;

typedef size_t (*HashFunc)(keytype key);  

//这个结构表示的是哈希表中的一个元素
//这个元素同时包含了键值对
typedef struct Hashelme{
    keytype key;
    valtype value;
    Stat  stat;
}Hashelme;

typedef struct Hashtable{
   Hashelme  data[HashMaxSize];
   size_t size;//哈希表当中元素的个数(【0,size)不能表示哈希表有效元素的区间
   HashFunc func;//这是一个函数指针,指向的是hash函数
}Hashtable;

void HashInit(Hashtable* ht);

void HashDestroy(Hashtable* ht);

void HashInsert(Hashtable* ht,keytype key,valtype value);

//此时我们根据key找到value就可以了
int HashFind(Hashtable* ht,keytype key,valtype* value);

void HashRemove(Hashtable* ht,keytype key);

test.c的实现如下:

#include "hash1.h"

size_t  HashDefault(keytype key){
    return key % HashMaxSize;
}

 void HashInit(Hashtable* ht)
{
    if(ht==NULL){
    return ;
   }
    ht->size=0;
    ht->func=HashDefault;
    size_t i=0;
    //将哈希表每一个位置初始化为空状态
    //表示从来没有被使用过
    for(i=0;i<HashMaxSize;i++){
    ht->data[i].stat=empty;
    }
    return;
}

void HashDestroy(Hashtable* ht)
{
    if(ht==NULL){
    return;
    }
   //将哈希表中的每一个位置都为无效状态
   size_t i=0;
   for(i=0;i<HashMaxSize;i++){
       ht->data[i].stat=empty;
   }
   ht->size=0;
   ht->func=NULL;
   return;
}

//插入数据
 void HashInsert(Hashtable* ht,keytype key,valtype value)
{
    if(ht==NULL){
    return;
    }

  //  1、判定当前hash表能否继续插入元素,假设负载因子为0.8
  if(ht->size>=0.8*HashMaxSize){
      return;//当前的hash表已经达到了负载因子的上限不能再继续进行插入了
  }
  //2、根据key计算出offset(由hash函数计算出有效位置的下标;
  size_t  offset=ht->func(key);
  //但是该位置可能之前已经被别的数据占据了,所以我们就要先判断出
  //该位置是否能够进行插入当前的数据,要是不能的话我们就进行offset进行往后查找
  while(1){
      //先判断当前计算出的位置能否放入当前的数据
      if(ht->data[offset].stat!=valid)
      {
     //一旦我们计算出的位置不是有效位置,那么我们就可以把数据进行插入了
     //这个就是我们处理哈希冲突的线性探测的方法;
     ht->data[offset].key=key;
     ht->data[offset].value=value;
     //插入完成之后将该位置设置成有效状态
     ht->data[offset].stat=valid;
     //然后将哈希表的有效元素+1
     ht->size++;
     return;
      }
      //走到这里就说明当前我们计算出的当前位置不能放置当前的数据
      //判断当前位置上的元素是否和我们要插入的元素相等
      else if(ht->data[offset].stat==valid && ht->data[offset].key==key)
      {
      //走到这里就说明存在相同的元素,
      //在这里我们约定这个哈希表中不存在重复的元素
      //所以我们就认为失败返回
      return;
      }
      //则更新offset的值进行往后查找
      else
      {
      offset++;
      if(offset>=HashMaxSize){
          //如果查找到hash的末尾还没有找到一个可插入的位置
          //那么我们就重置从头开始继续查找
          offset=0;
      }
      }//else结束
  }//while循环结束
}

void HashPrint(Hashtable* ht,const char* msg)
{
    printf("[%s]\n",msg);
    int i=0;
    for(;i<HashMaxSize;i++)
    {
    if(ht->data[i].stat==valid)
    {
        printf("(%d:%d,%d) ",i,ht->data[i].key,ht->data[i].value);
    }
    }
    printf("\n");
}

//此时我们根据key找到value就可以
int HashFind(Hashtable* ht,keytype key,valtype* value)
{
    if(ht==NULL){
    return 0;//非法输入
    }
    //判断当前哈希表中是否有效元素
    if(ht->size==0){
    return 0;//空哈希表
    }
    //根据当前元素计算出key值
    size_t offset=ht->func(key);
    //然后根据offset向后进行查找
    while(1)
    {
    //在当前元素的状态为有效元素
    if(ht->data[offset].stat==valid)
    {
        if(ht->data[offset].key==key)
        {
        //找到了
        *value=ht->data[offset].value;
        return 1;
        }
        //当前位置存放的不是待查找的元素,
        //更新offset继续进行查找
        else
        {
        offset++;
        if(offset>=HashMaxSize){
            //重新进行二次查找
            offset=0;
        }
        }
    }
        else if(ht->data[offset].stat==empty)
        {
        //说明待查找的元素不在hash表中,查找失败
        return 0;
        }
    }//while循环结束
    return 0;
}

 void HashRemove(Hashtable* ht,keytype key)
{
    if(ht==NULL)
    {
    return;
    }
    if(ht->size==0)
    {
    return;//空哈希表
    }
    int offset=ht->func(key);
    while(1)
    {
    if(ht->data[offset].stat==valid && ht->data[offset].key==key)
    {
        //找到了要删除的元素
        //将其状态置为可删除状态
        ht->data[offset].stat=deleted;
        ht->size--;
        return;
    }
    else if(ht->data[offset].stat==empty)
    {
        //没有找到要删除的元素
        return;
    }
    else
    {
        offset++;
        if(offset>=HashMaxSize)
        offset=0;
    }
    }//while循环结束
    return;
}



//////////////////////////////////////////////////////////
//////////////////test///////////////////////////////////
//////////////////////////////////////////////////////////

#define TEST_HEADER printf("\n==============%s===========\n",__FUNCTION__);

 void test1()
 {
     TEST_HEADER;
     Hashtable ht;
     HashInit(&ht);
     printf("expext size is 0,actual size is %d\n",ht.size);//在这里用的“."
     //因为ht在这里是一个常量而不是一个指针类型的;
     printf("expect func is %p,actual func is %p\n",HashDefault,ht.func);
 }


void test2()
{

     TEST_HEADER;
     Hashtable ht;
     HashInit(&ht);
     HashInsert(&ht,1,1);
     HashInsert(&ht,1,10);
     HashInsert(&ht,2,20);
     HashInsert(&ht,1000,100);
     HashInsert(&ht,2000,200);
     HashPrint(&ht,"插入5个元素");
}

void test3()
{

     TEST_HEADER;
     Hashtable ht;
     HashInit(&ht);

     HashInsert(&ht,1,1);
     HashInsert(&ht,1,10);
     HashInsert(&ht,2,20);
     HashInsert(&ht,1000,100);
     HashInsert(&ht,2000,200);
     HashPrint(&ht,"插入5个元素");
     printf("\n");

     valtype value;
     int ret=HashFind(&ht,1000,&value);
     printf("expct return is 1,actual return is %d\n",ret);
     printf("expect value is 100,actual value is %d\n",value);
     printf("\n");


     ret=HashFind(&ht,2000,&value);
     printf("expct return is 1,actual return is %d\n",ret);
     printf("expect value is 200,actual value is %d\n",value);

}

void test4()
{

     TEST_HEADER;
     Hashtable ht;
     HashInit(&ht);

     HashInsert(&ht,1,1);
     HashInsert(&ht,1,10);
     HashInsert(&ht,2,20);
     HashInsert(&ht,1000,100);
     HashInsert(&ht,2000,200);
     HashPrint(&ht,"插入5个元素");
     printf("\n");
     HashRemove(&ht,2);
     HashPrint(&ht,"删除元素2: ");

     HashRemove(&ht,1000);
     HashPrint(&ht,"删除元素1000: ");

}

int main()
 {
     test1();
     test2();
     test3();
     test4();
     return;
 }

最后执行的结果为:

这里写图片描述
第二种方法具体见下一篇博客!!

猜你喜欢

转载自blog.csdn.net/daboluo521/article/details/80418390