数据结构Hash--开放定址法

Hash,一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫做预映射pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数
开放定制法也是处理构造哈希表中关键字的哈希地址冲突的一种有效方法。
开放定址法就是从发生冲突的那个单元开始,按照一定的次序,从哈希表中查找出一个空闲的存储单元,把发生冲突的待插入元素存入到该空闲单元的一类处理冲突的方法。在开放定址法中,哈希表中的空闲单元(假定下标为i)不仅向哈希地址为i的同义词的元素开放,允许它们使用,也向发生冲突的其他元素开放,因它们 的哈希地址不为i,所以称之为非同义词元素。
总之,在开放定址法中,空闲单元不仅向同义词开放,也向发生冲突的非同义词开放,因此称之为开放定址法。
假设哈希表的地址集为0~n-1,冲突是指关键字得到的哈希地址为j(0<=j<=n-1)的位置上已经有关键字存在,则冲突处理就是为该关键字找到一个“空”的哈希地址。在处理冲突的过程中可能得到一个一个地址序列,Hi (i=1,2,3,4…k,),Hi介于0到n-1之间,即在处理哈希地址的冲突时,若得到另一个哈希地址H1仍然发生冲突,则再求下一个地址H2,若H2仍然冲突,则再求H3,以此类推,直至Hk不发生冲突为止,则Hk为记录在表中的地址。

开放定址法主要有三种方法,包括线性探测再散列(即线性探测法),二次探测再散列(即平方探测法)和伪随机探测再散列
这里代码实现的是其中的线性探测再散列:
例如在长度为11的哈希表中,已经填入关键字分别为17,60,29的记录(哈希函数为H(key) MOD 11),现有第四个记录,其关键字为38,由哈希函数得到的哈希地址为5,和关键字为60的哈希地址产生冲突;若用线性探测再散列的方法处理时,得到下一个地址为6,仍冲突;再求下一个地址7,仍然冲突;直到哈希地址为8的位置时,得到一个为“空”的位置,处理冲突的过程结束,并将38插入到哈希表中第8号位置。
这里写图片描述

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int KeyType;
typedef int ValueType;

enum Status
{
    EMPTY,//当前为空
    EXITS,//存在数
    DELETE,//删除了
};

typedef struct HashNode
{
    KeyType _key;//数值
    ValueType _value;//出现次数
    enum Status _status;//当前位置状态
}HashNode;

typedef struct HashTable
{
    HashNode* _tables;//数组
    size_t _size;//数组元素个数
    size_t _N;//数组长度
}HashTable;
void HashTableInit(HashTable* ht, size_t capacity)
{
    ht->_N = capacity;
    ht->_size = 0;
    ht->_tables = (HashNode*)malloc(sizeof(HashNode)*ht->_N);//给数组动态开辟一个空间
    for (int i = 0; i <(int)ht->_N; i++)//把数组置空
    {
        ht->_tables[i]._status = EMPTY;
    }
}
void ExpendCapacity(HashTable *ht)//给数组增容变为新数组
{
    if ((ht->_size * 10) < (ht->_N * 7))//如果当前的元素个数>数组长度的0.7增容减少哈希冲突
        return;
    HashTable newht;//建立新数组
    HashTableInit(&newht, ht->_N * 2);//给新数组初始化
    for (int i = 0; i <(int)ht->_N; i++)//把原数组的元素插入新数组
    {
        if (ht->_tables[i]._status == EXITS)
            HashTableInsert(&newht, ht->_tables[i]._key, ht->_tables[i]._value);
    }
    HashTableDestory(ht);//销毁原数组
    ht->_N = newht._N;
    ht->_size = newht._size;
    ht->_tables = newht._tables;
}
int HashTableFunc(KeyType key, size_t capacity)//决定插入元素的位置
{
    return key % capacity;
}
int HashTableInsert(HashTable* ht, KeyType key, ValueType value)//插入元素
{
    assert(ht);
    ExpendCapacity(ht);//首先判断是否会冲突过大,过大增容
    int index = HashTableFunc(key, ht->_N);//标记插入的位置
    int cur = index;
    int i = 1;
    while (1)
    {
        if (ht->_tables[index]._status != EXITS)//如果当前位置状态不为存在,为空或删除过数据
        {
            ht->_tables[index]._key = key;
            ht->_tables[index]._status = EXITS;
            ht->_tables[index]._value = value;
            ht->_size++;
            break;
        }
        index = cur;
        index = index + i * i;
        i++;
        if (index >= (int)ht->_N)
        {
            index = index % ht->_N;
        }
    }
    return 0;
}
HashNode* HashTableFind(HashTable* ht, KeyType key)//查找一个数是否在数组里
{
    int index = HashTableFunc(key, ht->_N);
    int i = 1;
    int cur = index;//cur标记插入位置
    while (1)
    {
        if (ht->_tables[index]._status == EMPTY)//如果当前位置状态为空返回
            return NULL;
        else if (ht->_tables[index]._key == key)//如果当前位置的值等于你要查找的值
        {
            if (ht->_tables[index]._status == DELETE)//如果当前位置的状态为删除说明数据不存在
                return NULL;
            else {
                return &ht->_tables[index];//存在
            }
        }
        else {
            index = cur;
            index = index + i * i;
            i++;
        }
    }
    return NULL;
}
int HashTableRemove(HashTable* ht, KeyType key)
{
    HashNode* cur = HashTableFind(ht, key);//先查找位置
    if (cur == NULL)
        return -1;
    else cur->_status = DELETE;//直接把状态置为删除
    return 0;
}
int HashTableDestory(HashTable* ht)//摧毁数组
{
    int i = 0;
    free(ht->_tables);
    ht->_tables = NULL;
    ht->_N = 0;
    ht->_size = 0;
    return 0;
}
void HashPrint(HashTable *ht)//打印数组
{
    int i = 0;
    for (i = 0; i < (int)ht->_N; i++)
    {
        if (ht->_tables[i]._status == EXITS)
            printf("[%d ]", ht->_tables[i]._key);
        else
            if (ht->_tables[i]._status == EMPTY)
            {
                printf("[NULL]");
            }
            else { printf("[Del]"); }
    }
    printf("\n");
}
void TestHash()
{
    HashTable ht;
    HashTableInit(&ht, 5);
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        HashTableInsert(&ht, i*i, 1);
    }
    HashPrint(&ht);
    for (i = 0; i < 10; i++)
    {
        HashTableRemove(&ht, i*i);
    }
    HashPrint(&ht);
    HashTableDestory(&ht);
}
int main()
{
    TestHash();
    getchar();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_40853073/article/details/80547396