【数据结构】哈希表的基本操作(二)·哈希桶处理哈希冲突

开散列:(链地址法也叫做开链法)首先对关键码集合用哈希函数计算哈希地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,每个桶中的元素通过单链表链接起来,链表的头结点存储在哈希表中,由此有一个形象的叫法叫做哈希桶。
这里写图片描述

test.h的实现

#pragma once 

#include <stdio.h>
#include <stdlib.h>
#include <stddef.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;
    struct Hashelme* next;
}Hashelme;

typedef struct Hashtable{
    //如果我们的hash桶中的链表是一个不带头结点的链表
    //我们就是用Hashelme*
    //如果是一个带头结点的链表
    //我们就直接用Hashelme
    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);

int HashFind(Hashtable* ht,keytype key,valtype* value);

void HashRemove(Hashtable* ht,keytype);

test.c的实现

#include "hash2.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<HashMaxSize;i++)
    {
    ht->data[i]=NULL;
    }
    return;
}

void   HashDestroyNode(Hashelme* p)
{
    free(p);
    p=NULL;
}
//销毁
void HashDestroy(Hashtable* ht)
{
    if(ht==NULL)
    {
    return;
    }
    ht->size=0;
    ht->func=NULL;
    //遍历所有的链表并且进行释放
    size_t i=0;
    for(;i<HashMaxSize;i++)
    {
    Hashelme* cur=ht->data[i];
        if(cur==NULL)
        {
        continue;
        }
    while(cur!=NULL)
    {
        Hashelme* pre=cur;
        HashDestroyNode(cur);
        cur=pre->next;
    }
    return;
    }//退出for循环
}

Hashelme* HashBucketFind(Hashelme* head, keytype to_find)
{
    Hashelme* cur=head;
    for(;cur!=NULL;cur=cur->next)
    {
    if(cur->key==to_find)
    {
        break;
    }
    }
    return cur;
}

 Hashelme* Createelme(keytype key,valtype value)
{
    Hashelme* newelme=(Hashelme*)malloc(sizeof(Hashelme));
    newelme->key=key;
    newelme->value=value;
    return newelme;
}

void HashInsert(Hashtable* ht,keytype key,valtype value)
{
    if(ht==NULL)
    {
    return;
    }
    if(ht->size>0.8*HashMaxSize)
    {
    //哈希表中的有效元素已经超过了负载因子的上限、
    //所以不能再继续的进行插入元素了
    return;
    }
    //走到这里说明我们还能继续进行插入元素,所以
    //我们首先应该计算出offset的值
    size_t offset=HashDefault(key);
    //在offset对应的链表中查找当前的key是否存在,如果
    //存在就认为查找失败
    Hashelme* ret=HashBucketFind(ht->data[offset],key);
    if(ret!=NULL)
    {
    //走到这里我们认为插入失败,所要插入的元素已经存在
    //插入元素失败,返回
    return;
    }
    //走到这里说明我们所要插入的元素不存在,就可以插入(使用头插的方法)
    Hashelme* new_elme=Createelme(key,value);
    //将新元素插入到当前的链表当中
    new_elme->next=ht->data[offset];
    //更新头结点的值为我们新插入的元素
    ht->data[offset]=new_elme;
    //给size进行+1操作
    ++ht->size;
}

void HashPrint(Hashtable* ht,const char* msg)
{
    printf("[%s]\n",msg);
    size_t i=0;
    for(;i<HashMaxSize;i++)
    {
    if(ht->data[i]!=NULL)
    {
        printf("i=%lu\n",i);
        Hashelme* cur=ht->data[i];
        for(;cur!=NULL;cur=cur->next)
        {
        printf("(%d:%d)\n",cur->key,cur->value);
        }
    }//if循环结束
    }//for循环结束
    printf("\n");
}

//查找元素是否存在于对应的链表中的函数,成功的话返回元素的节点
int HashFind(Hashtable* ht,keytype key,valtype* value)
{
    if(ht==NULL || value==NULL)
    {
    return 0;
    }
    if(ht->size==0)
    {
    return 0;
    }
    //1、计算出offset的值
    size_t offset=HashDefault(key);
    //2、找到offset的链表,遍历链表尝试找到其中的元素
     Hashelme* ret=HashBucketFind(ht->data[offset],key);
     if(ret==NULL)
     {
     //没有找到
     return 0;
     }
     else
     {
     //找到了
     *value=ret;
     return *value;
     }
}

int HashBucketFindEx(Hashelme* head,keytype to_find,Hashelme** prenode,Hashelme** curnode)
{
    Hashelme* cur=head;
    Hashelme* pre=NULL;
    for(;cur!=NULL;pre=cur,cur=cur->next)
    {
    if(cur->key==to_find)
    {
        //说明找到了
        break;
    }
    }
    if(cur==NULL)
    {
    //说明链表已经遍历完毕了
    return 0;
    }
    *prenode=pre;
    *curnode=cur;
    return 1;
}

void HashRemove1(Hashtable* ht,keytype key)
{
    if(ht==NULL)
    {
    return;
    }
    if(ht->size==0)
    {
    return;
    }
    //1、根据key计算出offset
    size_t offset=HashDefault(key);
    //2、通过offset找出相应的链表,在链表当中找到指定的元素
    //并且进行删除
    Hashelme* pre=NULL;
    Hashelme* cur=NULL;
    int ret=HashBucketFindEx(ht->data[offset],key,&pre,&cur);
    if(ret==0)
    {
    return;
    }
    else
    {
    //找到了
    if(pre==NULL)
    {
        //pre本来就是cur的前一个元素,那么我们要删除的就是头结点
        //我们必须更新头结点的指针
        ht->data[offset]=cur->next;
    }
    else
    {
        //cur 为待删除的元素,pre->next本来指向的是cur
        //如果cur被删除了,现在pre指向的是cur的下一个元素
        pre->next=cur->next;
    }
    HashDestroyNode(cur);
       //3、--size
       --ht->size;
    }
}

void HashRemove2(Hashtable* ht,keytype key)
{
    if(ht==NULL)
    {
    return;
    }
    if(ht->size==0)
    {
    return;
    }
    //1、根据key计算出offset
    size_t offset=HashDefault(key);
    //2、通过offset找出相应的链表,在链表当中找到指定的元素
    //并且进行删除
    Hashelme* pre=NULL;
    Hashelme* cur=ht->data[offset];
    while(cur!=NULL)
    {
    if(cur->key==key)
    {
        break;
    }
    pre=cur;
    cur=cur->next;
    }
    if(cur==NULL)
    {
    return;
    }
    if(pre==NULL)
    {
    Hashelme* p=ht->data[offset];
    ht->data[offset]=p->next;
    HashDestroyNode(p);
    }
    else
    {
    pre->next=cur->next;
    HashDestroyNode(cur);
    }
}
/////////////////////////////////////////////////////
/////////////////////////test///////////////////////
///////////////////////////////////////////////////


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

void test1()
{
    TEST_HEADER;
    Hashtable ht;
    HashInit(&ht);
    printf("expect size is 0,actual size is %d\n",ht.size);
    printf("expect func is %p,actual func is %p",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,"插入五个元素");
}

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,"插入五个元素");
    valtype value;
    int ret=0;
    ret= HashFind(&ht,1000,&value);
    printf("查找数据’1000‘的结果是: \n");
    printf("expect ret is 1,actual ret is %d\n",ret);
    printf("expect value is 10,actual value is %d\n",value);

    ret= HashFind(&ht ,4000,&value);
    printf("查找数据’4000‘的结果是: \n");
    printf("expect ret is 0,actual ret is %d\n",ret);
}

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,"插入五个元素");
    HashRemove2(&ht,1);
    HashPrint(&ht,"删除数据为1的元素后的结果为:");
    HashRemove2(&ht,2);
    HashPrint(&ht,"删除数据为2的元素后的哈希表:");
}

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

最后结果的实现

这里写图片描述

猜你喜欢

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