哈希表的基础操作总结(插入、查找、删除等)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hansionz/article/details/82052710
0.定义结构
typedef int HTKeyType;
typedef char HTValueType;
//枚举哈希表中每一个数据的三种状态
typedef enum State
{
    EMPTY,//空
    EXITST,//存在数据
    DELETE//已被删除
}State;
//定义哈希表中数据类型
typedef struct HashData
{
    State _state;//状态
    HTKeyType _key;//key-value
    HTValueType _value;//key-value
}HashData;
//定义哈希表的结构
typedef struct HashTable
{
    HashData* _table;//动态数组实现
    int _len;//表长
    int _size;//有效元素的个数
}HashTable;
1.声明函数接口
//哈希表的初始化
void HashTableInit(HashTable* ht, size_t len);
//销毁哈希表(malloc出来的空间)
void HashTableDestroy(HashTable* ht);
//向哈希表中插入一个值
int HashTableInsert(HashTable* ht, HTKeyType key, HTValueType value);
//在哈希表中删除一个值
int HashTableRemove(HashTable* ht, HTKeyType key);
//在哈希表中查找一个值
HashData* HashTableFind(HashTable* ht, HTKeyType key);
//判断哈希表是否为空(空返回0,非空返回1)
int HashTableEmpty(HashTable* ht);
//哈希表的有效元素个数
int HashTableSize(HashTable* ht);
//打印哈希表中的数据
void PrintHashTable(HashTable* ht);
2.各函数的实现
#include "HashTable.h"

//哈希表的初始化
void HashTableInit(HashTable* ht, size_t len)
{
    assert(ht);
    //动态开辟一个数组
    ht->_table = (HashData*)malloc(sizeof(HashData)*len);
    if (ht->_table == NULL)
    {
        perror("use malloc to create");
    }
    //首先将所有数据的状态都设置为空(EMPTY)
    for (int i = 0; i < ht->_len; i++)
    {
        ht->_table[i]._state = EMPTY;
    }
    ht->_len = len;//哈希表的长度
    ht->_size = 0;//有效元素的个数
}
//销毁哈希表(malloc出来的空间)
void HashTableDestroy(HashTable* ht)
{
    assert(ht);
    free(ht->_table);
    ht->_table = NULL;
    ht->_size = 0;
    ht->_len = 0;
}
//哈希函数,根据哈希函数得到一个地址(采用模留余数法)
int GetPosition(HashTable* ht, HTKeyType key)
{
    assert(ht);
    return key % (ht->_len);
}
//检查负载因子是否达到了上限(规定为0.7),如果达到上限,则要给哈希表扩容
void CheckCapacity(HashTable* ht)
{
    assert(ht);
    //思路:开辟一个新的哈希表,先遍历一遍旧哈希表,将所有的数据转移到新的哈希表
    //在将旧哈希表销毁,最后将新的哈希表给旧哈希表维护的指针
    HashTable newht;
    int len = 2 * ht->_len;//新表表长(按照2倍来计算比较合理,否则会太大太小)
    if ((ht->_size * 10) / ht->_len > 7)//负载因子计算(因为整数除以整数结果为整数,无法与浮点数作比较,所以同时扩大10倍)
    {
        HashTableInit(&newht, len);
        //遍历一遍旧哈希表,将所有的数据转移到新的哈希表
        for (int i = 0; i < ht->_len; i++)
        {
            if (ht->_table[i]._state == EXITST)
            {
                HashTableInsert(&newht, ht->_table[i]._key, ht->_table[i]._value);
            }
        }
        //销毁旧表
        HashTableDestroy(ht);
        //将新的哈希表给旧哈希表维护的指针
        ht->_table = newht._table;
        ht->_size = newht._size;
        ht->_len = newht._len;
    }
}
//向哈希表中插入一个值
int HashTableInsert(HashTable* ht, HTKeyType key, HTValueType value)
{
    assert(ht);
    //检查容量是否达到最大负载因子,达到则扩容
    CheckCapacity(ht);
    //1.确定插入的位置
    int index = GetPosition(ht, key);
    //2.如果有冲突,处理冲突(闭散列线性探测)
    while (ht->_table[index]._state == EXITST)
    {
        //如果表中有要插入的数据,则插入失败
        if (ht->_table[index]._key == key)
        {
            return 0;
        }
        //闭散列线性探测,找下一个插入位置
        else
        {
            ++index;
        }
    }
    //3.找到插入位置,直接插入
    ht->_table[index]._state = EXITST;
    ht->_table[index]._key = key;
    ht->_table[index]._value = value;
    ht->_size++;
}
//在哈希表中查找一个值
int HashTableRemove(HashTable* ht, HTKeyType key)
{
    assert(ht);
    //1.找到要删除元素的位置
    HashData* ret = HashTableFind(ht, key);
    //2.不为空则找到,直接修改状态删除,删除成功返回1
    if (ret)
    {
        ht->_size--;
        ret->_state = DELETE;
        return 1;
    }
    //删除失败返回0
    return 0;
}
//在哈希表中删除一个值
HashData* HashTableFind(HashTable* ht, HTKeyType key)
{
    assert(ht);
    //1.根据哈希哈数先确定哈希地址
    int index = GetPosition(ht, key);
    //2.如果有冲突,按照处理冲突的方法再去查找
    while (ht->_table[index]._state != EMPTY)
    {
        if (ht->_table[index]._key == key)
        {
            //找到数据并且状态为存在则返回它的地址
            if (ht->_table[index]._state == EXITST)
            {
                return &ht->_table[index];
            }
            //状态为删除状态,说明要找的数据已经被删除,直接返回
            else
            {
                return NULL;
            }
        }
        //按照处理冲突的方法线性探测继续查找
        else
        {
            ++index;
        }
    }
    //找到空说明没有找到
    return NULL;
}
//打印哈希表(方便测试)
void PrintHashTable(HashTable* ht)
{
    assert(ht);
    for (int i = 0; i < ht->_len; i++)
    {
        if (ht->_table[i]._state == EXITST)
        {
            printf("[%d](%d:%c)\n", i, ht->_table[i]._key, ht->_table[i]._value);
        }
    }
    printf("\n");
}
//判断哈希表是否为空(空返回0,非空返回1)
int HashTableEmpty(HashTable* ht)
{
    assert(ht);

    return ht->_size == 0 ? 0 : 1;
}
//哈希表的有效元素个数
int HashTableSize(HashTable* ht)
{
    assert(ht);

    return ht->_size;
}

下边是测试代码:

#include "HashTable.h"

void TestHashTable()
{
    HashTable ht;
    HashTableInit(&ht, 5);
    HashTableInsert(&ht, 15, 'f');
    HashTableInsert(&ht, 24, 't');
    HashTableInsert(&ht, 56, 's');
    HashTableInsert(&ht, 71, 's');
    HashTableInsert(&ht, 23, 's');
    HashTableInsert(&ht, 46, 's');
    HashData* ret = HashTableFind(&ht, 71);
    printf("state:%d,key;%d,value:%c\n", ret->_state, ret->_key, ret->_value);
    printf("哈希表的大小:%d\n", HashTableSize(&ht));
    HashTableRemove(&ht, 15);
    printf("哈希表的大小:%d\n", HashTableSize(&ht));
    HashTableInsert(&ht, 59, 's');
    HashTableInsert(&ht, 31, 's');
    printf("哈希表的大小:%d\n", HashTableSize(&ht));
    PrintHashTable(&ht);
    HashTableDestroy(&ht);
}
int main()
{
    TestHashTable();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/hansionz/article/details/82052710